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