Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 8.1k
Zend: Add object_init_with_constructor() API#14440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
5aa6d5ab7021396a2ef11cebc0a2d50f1c35b2946d6d98dce3f6e96ebda84f4375515194724be0f2a38e610ca739ad870aFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1846,6 +1846,74 @@ ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /* | ||
| } | ||
| /* }}} */ | ||
| ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params) /* {{{ */ | ||
| { | ||
| zend_result status = _object_and_properties_init(arg, class_type, NULL); | ||
| if (UNEXPECTED(status == FAILURE)) { | ||
| ZVAL_UNDEF(arg); | ||
| return FAILURE; | ||
| } | ||
| zend_object *obj = Z_OBJ_P(arg); | ||
| zend_function *constructor = obj->handlers->get_constructor(obj); | ||
| if (constructor == NULL) { | ||
| /* The constructor can be NULL for 2 different reasons: | ||
| * - It is not defined | ||
| * - We are not allowed to call the constructor (e.g. private, or internal opaque class) | ||
| * and an exception has been thrown | ||
| * in the former case, we are (mostly) done and the object is initialized, | ||
| * in the latter we need to destroy the object as initialization failed | ||
| */ | ||
| if (UNEXPECTED(EG(exception))) { | ||
| zval_ptr_dtor(arg); | ||
| ZVAL_UNDEF(arg); | ||
| return FAILURE; | ||
| } | ||
| /* Surprisingly, this is the only case where internal classes will allow to pass extra arguments | ||
| * 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)); | ||
| zend_string_release(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; | ||
| } | ||
| } | ||
| /* A constructor should not return a value, however if an exception is thrown | ||
| * zend_call_known_function() will set the retval to IS_UNDEF */ | ||
| zval retval; | ||
| zend_call_known_function( | ||
| constructor, | ||
| obj, | ||
| class_type, | ||
| &retval, | ||
| param_count, | ||
| params, | ||
| named_params | ||
| ); | ||
| if (Z_TYPE(retval) == IS_UNDEF) { | ||
| /* 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 { | ||
| /* Unlikely, but user constructors may return any value they want */ | ||
| zval_ptr_dtor(&retval); | ||
| return SUCCESS; | ||
| } | ||
| } | ||
| /* }}} */ | ||
| ZEND_API void object_init(zval *arg) /* {{{ */ | ||
| { | ||
| ZVAL_OBJ(arg, zend_objects_new(zend_standard_class_def)); | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| --TEST-- | ||
| Zend: Test object_init_with_constructor() API | ||
| --EXTENSIONS-- | ||
| zend_test | ||
| sysvmsg | ||
| --FILE-- | ||
| <?php | ||
| class PrivateUser { | ||
| private function __construct() { | ||
| return new stdClass(); | ||
| } | ||
| public function __destruct() { | ||
| echo 'Destructor for ', __CLASS__, PHP_EOL; | ||
| } | ||
| } | ||
| class ThrowingUser { | ||
| public function __construct() { | ||
| throw new Exception("Don't construct"); | ||
| } | ||
| public function __destruct() { | ||
| echo 'Destructor for ', __CLASS__, PHP_EOL; | ||
| } | ||
| } | ||
| abstract class AbstractClass { | ||
| public function __construct() { | ||
| return new stdClass(); | ||
| } | ||
| public function __destruct() { | ||
| echo 'Destructor for ', __CLASS__, PHP_EOL; | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| echo "Testing impossible initializations\n"; | ||
| try { | ||
| $o = zend_object_init_with_constructor("_ZendTestInterface"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("_ZendTestTrait"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("ZendTestUnitEnum"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("AbstractClass"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("SysvMessageQueue"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("PrivateUser"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("ThrowingUser"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| echo "Testing param passing\n"; | ||
| try { | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorArgs"); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", "str", 5); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| try { | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", 5, string_param: "str", unused_param: 15.3); | ||
| var_dump($o); | ||
| unset($o); | ||
| } catch (\Throwable $e) { | ||
| echo $e::class, ': ', $e->getMessage(), PHP_EOL; | ||
| } | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", 5, string_param: "str"); | ||
| var_dump($o); | ||
| unset($o); | ||
| echo "Passing too many args to constructor\n"; | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", 5, "str", 'unused_param'); | ||
| var_dump($o); | ||
| unset($o); | ||
| echo "Testing class with defined constructor and no params\n"; | ||
| $o = zend_object_init_with_constructor("TestUserWithConstructorNoParams"); | ||
| var_dump($o); | ||
| unset($o); | ||
| ?> | ||
| --EXPECT-- | ||
| Testing impossible initializations | ||
| Error: Cannot instantiate interface _ZendTestInterface | ||
| Error: Cannot instantiate trait _ZendTestTrait | ||
| Error: Cannot instantiate enum ZendTestUnitEnum | ||
| Error: Cannot instantiate abstract class AbstractClass | ||
| Error: Cannot directly construct SysvMessageQueue, use msg_get_queue() instead | ||
| Error: Call to private PrivateUser::__construct() from global scope | ||
| Exception: Don't construct | ||
| Testing param passing | ||
| ArgumentCountError: Too few arguments to function TestUserWithConstructorArgs::__construct(), 0 passed and exactly 2 expected | ||
| TypeError: TestUserWithConstructorArgs::__construct(): Argument #1 ($int_param) must be of type int, string given | ||
| Error: Unknown named parameter $unused_param | ||
| object(TestUserWithConstructorArgs)#1 (0) { | ||
| } | ||
| Destructor for TestUserWithConstructorArgs | ||
| Passing too many args to constructor | ||
| object(TestUserWithConstructorArgs)#1 (0) { | ||
| } | ||
| Destructor for TestUserWithConstructorArgs | ||
| Testing class with defined constructor and no params | ||
| object(TestUserWithConstructorNoParams)#1 (0) { | ||
| } | ||
| Destructor for TestUserWithConstructorNoParams |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.