2525#include "zend_hash.h"
2626#include "zend_modules.h"
2727#include "zend_extensions.h"
28+ #include "zend_attributes.h"
2829#include "zend_constants.h"
2930#include "zend_interfaces.h"
3031#include "zend_exceptions.h"
@@ -1845,27 +1846,38 @@ ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /*
18451846
18461847ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params) /* {{{ */
18471848{
1849+ zend_function *constructor = class_type->constructor;
1850+ if (UNEXPECTED(constructor == NULL)) {
1851+ const zend_attribute *non_instantiable_class = zend_get_attribute_str(class_type->attributes, ZEND_STRL("noninstantiableclass"));
1852+ ZEND_ASSERT(non_instantiable_class);
1853+ zend_string *msg = Z_STR(non_instantiable_class->args[0].value);
1854+ zend_throw_error(NULL, "%s", ZSTR_VAL(msg));
1855+ ZVAL_UNDEF(arg);
1856+ return FAILURE;
1857+ }
1858+
1859+ if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) {
1860+ /* Use zend_bad_constructor_call() somehow? */
1861+ zend_throw_error(
1862+ NULL,
1863+ "Call to %s %s::%s() from global scope",
1864+ zend_visibility_string(class_type->constructor->common.fn_flags),
1865+ ZSTR_VAL(class_type->constructor->common.scope->name),
1866+ ZSTR_VAL(class_type->constructor->common.function_name)
1867+ );
1868+ ZVAL_UNDEF(arg);
1869+ return FAILURE;
1870+ }
1871+
18481872 zend_result status = _object_and_properties_init(arg, class_type, NULL);
18491873 if (UNEXPECTED(status == FAILURE)) {
18501874 ZVAL_UNDEF(arg);
18511875 return FAILURE;
18521876 }
18531877 zend_object *obj = Z_OBJ_P(arg);
1854- zend_function *constructor = obj->handlers->get_constructor(obj);
1855- if (constructor == NULL) {
1856- /* The constructor can be NULL for 2 different reasons:
1857- * - It is not defined
1858- * - We are not allowed to call the constructor (e.g. private, or internal opaque class)
1859- * and an exception has been thrown
1860- * in the former case, we are (mostly) done and the object is initialized,
1861- * in the latter we need to destroy the object as initialization failed
1862- */
1863- if (UNEXPECTED(EG(exception))) {
1864- zval_ptr_dtor(arg);
1865- ZVAL_UNDEF(arg);
1866- return FAILURE;
1867- }
18681878
1879+ /* Fake constructor means we don't need to call the constructor actually */
1880+ if (zend_is_pass_function(constructor)) {
18691881 /* Surprisingly, this is the only case where internal classes will allow to pass extra arguments
18701882 * However, if there are named arguments (and it is not empty),
18711883 * an Error must be thrown to be consistent with new ClassName() */
@@ -1884,6 +1896,7 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c
18841896 return SUCCESS;
18851897 }
18861898 }
1899+
18871900 /* A constructor should not return a value, however if an exception is thrown
18881901 * zend_call_known_function() will set the retval to IS_UNDEF */
18891902 zval retval;
@@ -3510,6 +3523,11 @@ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig
35103523 if (class_entry->info.internal.builtin_functions) {
35113524 zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type);
35123525 }
3526+
3527+ /* Assign the pass function as a default constructor */
3528+ if (class_entry->constructor == NULL) {
3529+ class_entry->constructor = (zend_function *) &zend_pass_function;
3530+ }
35133531
35143532 lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT);
35153533 lowercase_name = zend_new_interned_string(lowercase_name);
0 commit comments