Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1890,19 +1890,21 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c
* However, if there are named arguments (and it is not empty),
* an Error must be thrown to be consistent with new ClassName() */
if (UNEXPECTED(named_params != NULL && zend_hash_num_elements(named_params) != 0)) {
/* Throw standard Error */
zend_string *arg_name = NULL;
zend_hash_get_current_key(named_params, &arg_name, /* num_index */ NULL);
ZEND_ASSERT(arg_name != NULL);
zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(arg_name));
/* Do not call destructor, free object, and set arg to IS_UNDEF */
zend_object_store_ctor_failed(obj);
zval_ptr_dtor(arg);
ZVAL_UNDEF(arg);
return FAILURE;
} else {
return SUCCESS;
/* As named_arguments may contain positional args we need to check there is actually a string key */
ZEND_HASH_FOREACH_STR_KEY(named_params, zend_string *arg_name) {
if (arg_name == NULL) {
continue;
}
/* Throw standard Error */
zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(arg_name));
/* Do not call destructor, free object, and set arg to IS_UNDEF */
zend_object_store_ctor_failed(obj);
zval_ptr_dtor(arg);
ZVAL_UNDEF(arg);
return FAILURE;
} ZEND_HASH_FOREACH_END();
}
return SUCCESS;
}
/* A constructor should not return a value, however if an exception is thrown
* zend_call_known_function() will set the retval to IS_UNDEF */
Expand Down
22 changes: 22 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,28 @@ static ZEND_FUNCTION(zend_object_init_with_constructor)
ZVAL_COPY_VALUE(return_value, &obj);
}


/* Instantiate a class and run the constructor via object_init_with_constructor */
static ZEND_FUNCTION(zend_object_init_with_constructor_array)
{
zend_class_entry *ce = NULL;
HashTable *args;

ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_CLASS(ce)
Z_PARAM_ARRAY_HT(args)
ZEND_PARSE_PARAMETERS_END();

zval obj;
/* We don't use return_value directly to check for memory leaks of the API on failure */
zend_result status = object_init_with_constructor(&obj, ce, 0, NULL, args);
if (status == FAILURE) {
RETURN_THROWS();
}
ZEND_ASSERT(!EG(exception));
ZVAL_COPY_VALUE(return_value, &obj);
}

static ZEND_FUNCTION(zend_get_unit_enum)
{
ZEND_PARSE_PARAMETERS_NONE();
Expand Down
1 change: 1 addition & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ function zend_get_current_func_name(): string {}
function zend_call_method(object|string $obj_or_class, string $method, mixed $arg1 = UNKNOWN, mixed $arg2 = UNKNOWN): mixed {}

function zend_object_init_with_constructor(string $class, mixed ...$args): mixed {}
function zend_object_init_with_constructor_array(string $class, array $args): mixed {}

function zend_test_zend_ini_parse_quantity(string $str): int {}
function zend_test_zend_ini_parse_uquantity(string $str): int {}
Expand Down
9 changes: 8 additions & 1 deletion ext/zend_test/test_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ext/zend_test/tests/optimizer_register_pass.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ if (getenv("SKIP_PRELOAD")) {
}
?>
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache=
opcache.file_cache_only=0
Expand Down
1 change: 0 additions & 1 deletion ext/zend_test/tests/zend_object_init_with_constructor.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Zend: Test object_init_with_constructor() API
--EXTENSIONS--
zend_test
sysvmsg
--FILE--
<?php

Expand Down
152 changes: 152 additions & 0 deletions ext/zend_test/tests/zend_object_init_with_constructor_array.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
--TEST--
Zend: Test object_init_with_constructor() API
--EXTENSIONS--
zend_test
--FILE--
<?php

class TestUserWithConstructorArgs {
public function __construct(int $int_param, string $string_param) {
return new stdClass();
}
public function __destruct() {
echo 'Destructor for ', __CLASS__, PHP_EOL;
}
}

class TestUserWithConstructorNoParams {
public function __construct() {
return new stdClass();
}
public function __destruct() {
echo 'Destructor for ', __CLASS__, PHP_EOL;
}
}

class TestUserWithoutConstructor {
public function __destruct() {
echo 'Destructor for ', __CLASS__, PHP_EOL;
}
}

$args = [5, 'str'];
$argsNamed = [5, 'string_param' => 'str'];
$argsNamedExtraUnknown = [5, 'string_param' => 'str', 'unknown' => 'unknown'];

echo "Testing param passing\n";
echo "Test TestUserWithConstructorArgs:\n";
$o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $args);
var_dump($o);
unset($o);

$o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $argsNamed);
var_dump($o);
unset($o);

try {
$o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $argsNamedExtraUnknown);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "Test TestUserWithConstructorNoParams:\n";
try {
$o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $args);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

try {
$o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $argsNamed);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

try {
$o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $argsNamedExtraUnknown);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "Test TestUserWithoutConstructor:\n";
$o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $args);
var_dump($o);
unset($o);

try {
$o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $argsNamed);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

try {
$o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $argsNamedExtraUnknown);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "Test _ZendTestMagicCall (internal class no ctor):\n";
try {
$o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $args);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

try {
$o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $argsNamed);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

try {
$o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $argsNamedExtraUnknown);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
Testing param passing
Test TestUserWithConstructorArgs:
object(TestUserWithConstructorArgs)#1 (0) {
}
Destructor for TestUserWithConstructorArgs
object(TestUserWithConstructorArgs)#1 (0) {
}
Destructor for TestUserWithConstructorArgs
Error: Unknown named parameter $unknown
Test TestUserWithConstructorNoParams:
object(TestUserWithConstructorNoParams)#1 (0) {
}
Destructor for TestUserWithConstructorNoParams
Error: Unknown named parameter $string_param
Error: Unknown named parameter $string_param
Test TestUserWithoutConstructor:
object(TestUserWithoutConstructor)#3 (0) {
}
Destructor for TestUserWithoutConstructor
Error: Unknown named parameter $string_param
Error: Unknown named parameter $string_param
Test _ZendTestMagicCall (internal class no ctor):
object(_ZendTestMagicCall)#2 (0) {
}
Error: Unknown named parameter $string_param
Error: Unknown named parameter $string_param
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,83 @@ try {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "\n#### Splatting arrays with string key ####\n";
$a = ['unknown' => "hello"];
echo "Userland class:\n";
echo "Using new:\n";
try {
$o = new TestUserWithoutConstructor(...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "Using zend_object_init_with_constructor():\n";
try {
$o = zend_object_init_with_constructor("TestUserWithoutConstructor", ...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "Internal class:\n";
echo "Using new:\n";
try {
$o = new _ZendTestMagicCall(...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "Using zend_object_init_with_constructor():\n";
try {
$o = zend_object_init_with_constructor("_ZendTestMagicCall", ...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}


echo "\n#### Splatting arrays with int key ####\n";
$a = [1 => "hello"];
echo "Userland class:\n";
echo "Using new:\n";
try {
$o = new TestUserWithoutConstructor(...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "Using zend_object_init_with_constructor():\n";
try {
$o = zend_object_init_with_constructor("TestUserWithoutConstructor", ...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo "Internal class:\n";
echo "Using new:\n";
try {
$o = new _ZendTestMagicCall(...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "Using zend_object_init_with_constructor():\n";
try {
$o = zend_object_init_with_constructor("_ZendTestMagicCall", ...$a);
var_dump($o);
unset($o);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
#### Passing no args ####
Expand Down Expand Up @@ -147,3 +224,33 @@ Using new:
Error: Unknown named parameter $unknown_param
Using zend_object_init_with_constructor():
Error: Unknown named parameter $unknown_param

#### Splatting arrays with string key ####
Userland class:
Using new:
Error: Unknown named parameter $unknown
Using zend_object_init_with_constructor():
Error: Unknown named parameter $unknown
Internal class:
Using new:
Error: Unknown named parameter $unknown
Using zend_object_init_with_constructor():
Error: Unknown named parameter $unknown

#### Splatting arrays with int key ####
Userland class:
Using new:
object(TestUserWithoutConstructor)#2 (0) {
}
Destructor for TestUserWithoutConstructor
Using zend_object_init_with_constructor():
object(TestUserWithoutConstructor)#2 (0) {
}
Destructor for TestUserWithoutConstructor
Internal class:
Using new:
object(_ZendTestMagicCall)#2 (0) {
}
Using zend_object_init_with_constructor():
object(_ZendTestMagicCall)#2 (0) {
}
Loading