From 45500c8fad371ef32932718dce220bd772ed8f2a Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 11 Sep 2025 09:42:51 +0200 Subject: [PATCH 1/3] core: set zend_pass_function as default constructor This is to make the semantics of when a class cannot be instiantiated obvious rather than needed to remember to call the get_constructor() handler. For this we create a new NonInstantiableClass attribute that can be set to mark classes which shouldn't be instantiable with a message and set the constructor pointer to NULL --- UPGRADING | 14 ++ Zend/Optimizer/escape_analysis.c | 4 +- Zend/Optimizer/zend_inference.c | 2 +- .../tests/enum/no-new-through-reflection.phpt | 6 +- Zend/tests/traits/bug60173.phpt | 2 +- Zend/zend_API.c | 46 ++++-- Zend/zend_attributes.c | 22 +++ Zend/zend_attributes.stub.php | 10 ++ Zend/zend_attributes_arginfo.h | 31 +++- Zend/zend_closures.c | 9 +- Zend/zend_closures.stub.php | 1 + Zend/zend_closures_arginfo.h | 10 +- Zend/zend_compile.c | 13 +- Zend/zend_compile.h | 7 + Zend/zend_execute.c | 2 +- Zend/zend_execute.h | 3 - Zend/zend_generators.c | 10 +- Zend/zend_generators.stub.php | 1 + Zend/zend_generators_arginfo.h | 10 +- Zend/zend_inheritance.c | 2 +- Zend/zend_objects_API.h | 20 +++ Zend/zend_vm_def.h | 17 ++- Zend/zend_vm_execute.h | 138 +++++++++++++----- build/gen_stub.php | 8 + ext/curl/curl.stub.php | 4 + ext/curl/curl_arginfo.h | 34 ++++- ext/curl/interface.c | 7 - ext/curl/multi.c | 7 +- ext/curl/share.c | 13 +- ext/dba/dba.c | 8 +- ext/dba/dba.stub.php | 1 + ext/dba/dba_arginfo.h | 10 +- ext/ffi/ffi.c | 13 +- ext/ffi/ffi.stub.php | 3 + ext/ffi/ffi_arginfo.h | 26 +++- ext/ftp/ftp.stub.php | 1 + ext/ftp/ftp_arginfo.h | 10 +- ext/ftp/php_ftp.c | 6 - ext/gd/gd.c | 14 -- ext/gd/gd.stub.php | 2 + ext/gd/gd_arginfo.h | 18 ++- ext/ldap/ldap.c | 18 --- ext/ldap/ldap.stub.php | 3 + ext/ldap/ldap_arginfo.h | 26 +++- ext/mysqli/mysqli.c | 18 ++- ext/odbc/odbc.stub.php | 2 + ext/odbc/odbc_arginfo.h | 18 ++- ext/odbc/php_odbc.c | 14 -- ext/opcache/zend_file_cache.c | 10 +- ext/openssl/openssl.c | 27 ---- ext/openssl/openssl.stub.php | 4 + ext/openssl/openssl_arginfo.h | 34 ++++- ext/pdo/pdo_dbh.c | 13 +- ext/pdo/pdo_stmt.c | 27 ++-- ext/pdo/pdo_stmt.stub.php | 1 + ext/pdo/pdo_stmt_arginfo.h | 10 +- ext/pdo/tests/pdo_036.phpt | 2 +- .../tests/pdo_fetch_class_opaque_object.phpt | 32 +--- ext/pdo/tests/pdorow.phpt | 2 +- ext/pgsql/pgsql.c | 39 ++--- ext/pgsql/pgsql.stub.php | 3 + ext/pgsql/pgsql_arginfo.h | 26 +++- .../pg_fetch_object_with_abstract_class.phpt | 4 +- ext/reflection/php_reflection.c | 91 ++++++------ ext/reflection/tests/007.phpt | 6 +- .../ReflectionClass_newInstanceArgs_001.phpt | 4 +- ...onClass_newInstanceWithoutConstructor.phpt | 2 +- .../ReflectionClass_newInstance_001.phpt | 4 +- ext/reflection/tests/bug52854.phpt | 3 +- ext/reflection/tests/bug64007.phpt | 8 +- ext/shmop/shmop.c | 7 - ext/shmop/shmop.stub.php | 1 + ext/shmop/shmop_arginfo.h | 10 +- ext/soap/soap.c | 16 -- ext/soap/soap.stub.php | 2 + ext/soap/soap_arginfo.h | 18 ++- ext/sockets/sockets.c | 13 +- ext/sockets/sockets.stub.php | 2 + ext/sockets/sockets_arginfo.h | 18 ++- ext/standard/basic_functions.c | 1 + ext/standard/dir.c | 8 +- ext/standard/dir.stub.php | 1 + ext/standard/dir_arginfo.h | 10 +- ...flection_create_instance_no_construct.phpt | 17 +-- ext/sysvmsg/sysvmsg.c | 9 +- ext/sysvmsg/sysvmsg.stub.php | 1 + ext/sysvmsg/sysvmsg_arginfo.h | 10 +- ext/sysvsem/sysvsem.c | 9 +- ext/sysvsem/sysvsem.stub.php | 1 + ext/sysvsem/sysvsem_arginfo.h | 10 +- ext/sysvshm/sysvshm.c | 9 +- ext/sysvshm/sysvshm.stub.php | 1 + ext/sysvshm/sysvshm_arginfo.h | 10 +- ext/xml/xml.c | 7 - ext/xml/xml.stub.php | 1 + ext/xml/xml_arginfo.h | 10 +- .../zend_object_init_with_constructor.phpt | 10 ++ ext/zlib/zlib.c | 13 +- ext/zlib/zlib.stub.php | 2 + ext/zlib/zlib_arginfo.h | 18 ++- sapi/fuzzer/fuzzer-sapi.c | 11 +- 101 files changed, 797 insertions(+), 475 deletions(-) diff --git a/UPGRADING b/UPGRADING index ffdf738828cf..e7975d333a18 100644 --- a/UPGRADING +++ b/UPGRADING @@ -43,6 +43,10 @@ PHP 8.6 UPGRADE NOTES . MessageFormatter::parse() and parseMessage() now return PHP_INT_MIN as int, rather than float, on 64-bit platforms when parsing integer values. +- MySQLi: + . mysqli_fetch_object() no longer accepts classes with non-public + constructors. + - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is lower than zero or greater than platform's UINT_MAX. @@ -54,6 +58,10 @@ PHP 8.6 UPGRADE NOTES execution error occurs (e.g. malformed UTF-8 input with the /u modifier). This is consistent with other preg_* functions. +- PDO: + . PDOStatement::fetchObject() no longer accepts classes with non-public + constructors. + - Phar: . Phar::mungServer() now raises a ValueError when an invalid argument value is passed instead of being silently ignored. @@ -69,6 +77,8 @@ PHP 8.6 UPGRADE NOTES $constructor_args argument instead of $class. Errors raised when the requested class is not instantiable (abstract, interface, enum) now surface before the row is fetched. + . pg_fetch_object() no longer accepts classes with non-public + constructors. - Posix: . posix_access() now raises a ValueError when an invalid $flags @@ -76,6 +86,10 @@ PHP 8.6 UPGRADE NOTES . posix_mkfifo() now raises a ValueError when an invalid $permissions argument value is passed. +- Reflection: + . ReflectionClass::newInstanceWithoutConstructor() no longer accepts + classes with non-public constructors. + - Session: . Setting session.cookie_path, session.cookie_domain, or session.cache_limiter to a value containing null bytes now emits a warning and leaves the setting diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index 8dbd6855d68d..ccdcb0dee6f8 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -167,7 +167,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object - && !ce->constructor + && (!ce->constructor || zend_is_pass_function(ce->constructor)) && !ce->destructor && !ce->__get && !ce->__set @@ -236,7 +236,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object - && !ce->constructor + && (!ce->constructor || zend_is_pass_function(ce->constructor)) && !ce->destructor && !ce->__get && !ce->__set diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 05d33d3d75fb..aba5b0eb8818 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3393,7 +3393,7 @@ static zend_always_inline zend_result _zend_update_type_info( } /* New objects without constructors cannot escape. */ if (ce - && !ce->constructor + && (!ce->constructor || zend_is_pass_function(ce->constructor)) && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor) { tmp &= ~MAY_BE_RCN; diff --git a/Zend/tests/enum/no-new-through-reflection.phpt b/Zend/tests/enum/no-new-through-reflection.phpt index 9a92559cd3d6..5ba61dc992d7 100644 --- a/Zend/tests/enum/no-new-through-reflection.phpt +++ b/Zend/tests/enum/no-new-through-reflection.phpt @@ -7,10 +7,10 @@ enum Foo {} try { (new \ReflectionClass(Foo::class))->newInstanceWithoutConstructor(); -} catch (Error $e) { - echo $e->getMessage() . "\n"; +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage() . "\n"; } ?> --EXPECT-- -Cannot instantiate enum Foo +ReflectionException: Class Foo cannot be instantiated manually diff --git a/Zend/tests/traits/bug60173.phpt b/Zend/tests/traits/bug60173.phpt index be7f652bf93b..664484cefe3e 100644 --- a/Zend/tests/traits/bug60173.phpt +++ b/Zend/tests/traits/bug60173.phpt @@ -9,7 +9,7 @@ $rc = new ReflectionClass('foo'); $rc->newInstance(); ?> --EXPECTF-- -Fatal error: Uncaught Error: Cannot instantiate trait foo in %s:%d +Fatal error: Uncaught ReflectionException: Class foo cannot be instantiated manually in %s:%d Stack trace: #0 %s(%d): ReflectionClass->newInstance() #1 {main} diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 65834adbafff..e16d02ade43d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -25,6 +25,7 @@ #include "zend_hash.h" #include "zend_modules.h" #include "zend_extensions.h" +#include "zend_attributes.h" #include "zend_constants.h" #include "zend_interfaces.h" #include "zend_exceptions.h" @@ -1845,27 +1846,38 @@ 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_function *constructor = class_type->constructor; + if (UNEXPECTED(constructor == NULL)) { + const zend_attribute *non_instantiable_class = zend_get_attribute_str(class_type->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + ZVAL_UNDEF(arg); + return FAILURE; + } + + if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) { + /* Use zend_bad_constructor_call() somehow? */ + zend_throw_error( + NULL, + "Call to %s %s::%s() from global scope", + zend_visibility_string(class_type->constructor->common.fn_flags), + ZSTR_VAL(class_type->constructor->common.scope->name), + ZSTR_VAL(class_type->constructor->common.function_name) + ); + ZVAL_UNDEF(arg); + return FAILURE; + } + 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; - } + /* Fake constructor means we don't need to call the constructor actually */ + if (zend_is_pass_function(constructor)) { /* 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() */ @@ -1884,6 +1896,7 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c 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; @@ -3510,6 +3523,11 @@ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig if (class_entry->info.internal.builtin_functions) { zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); } + + /* Assign the pass function as a default constructor */ + if (class_entry->constructor == NULL) { + class_entry->constructor = (zend_function *) &zend_pass_function; + } lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); lowercase_name = zend_new_interned_string(lowercase_name); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 71f0d788e921..c477d2d1f9e1 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -32,6 +32,7 @@ ZEND_API zend_class_entry *zend_ce_override; ZEND_API zend_class_entry *zend_ce_deprecated; ZEND_API zend_class_entry *zend_ce_nodiscard; ZEND_API zend_class_entry *zend_ce_delayed_target_validation; +ZEND_API zend_class_entry *zend_ce_non_instantiable_class; static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; @@ -264,6 +265,24 @@ ZEND_METHOD(NoDiscard, __construct) } } +ZEND_METHOD(NonInstantiableClass, __construct) +{ + zend_string *message = NULL; + zval value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(message) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_STR(&value, message); + zend_update_property_ex(zend_ce_non_instantiable_class, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); + + /* The assignment might fail due to 'readonly'. */ + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } +} + static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { if (attributes) { @@ -605,6 +624,9 @@ void zend_register_attribute_ce(void) zend_ce_delayed_target_validation = register_class_DelayedTargetValidation(); attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); + + zend_ce_non_instantiable_class = register_class_NonInstantiableClass(); + attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index ded9c89593a3..e6680662dc01 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -103,3 +103,13 @@ public function __construct(?string $message = null) {} */ #[Attribute(Attribute::TARGET_ALL)] final class DelayedTargetValidation {} + +/** + * @strict-properties + */ +#[Attribute(Attribute::TARGET_CLASS)] +final class NonInstantiableClass { + public readonly string $message; + + public function __construct(string $message) {} +} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 54a66af29966..a1b177fa3ff7 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */ + * Stub hash: 674dafdc1e2bdff88a7af7b2c6de13284ec2c445 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -33,6 +33,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NonInstantiableClass___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_METHOD(Attribute, __construct); ZEND_METHOD(ReturnTypeWillChange, __construct); ZEND_METHOD(AllowDynamicProperties, __construct); @@ -43,6 +47,7 @@ ZEND_METHOD(SensitiveParameterValue, __debugInfo); ZEND_METHOD(Override, __construct); ZEND_METHOD(Deprecated, __construct); ZEND_METHOD(NoDiscard, __construct); +ZEND_METHOD(NonInstantiableClass, __construct); static const zend_function_entry class_Attribute_methods[] = { ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC) @@ -86,6 +91,11 @@ static const zend_function_entry class_NoDiscard_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_NonInstantiableClass_methods[] = { + ZEND_ME(NonInstantiableClass, __construct, arginfo_class_NonInstantiableClass___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -291,3 +301,22 @@ static zend_class_entry *register_class_DelayedTargetValidation(void) return class_entry; } + +static zend_class_entry *register_class_NonInstantiableClass(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "NonInstantiableClass", class_NonInstantiableClass_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval property_message_default_value; + ZVAL_UNDEF(&property_message_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zend_string *attribute_name_Attribute_class_NonInstantiableClass_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); + zend_attribute *attribute_Attribute_class_NonInstantiableClass_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_NonInstantiableClass_0, 1); + zend_string_release_ex(attribute_name_Attribute_class_NonInstantiableClass_0, true); + ZVAL_LONG(&attribute_Attribute_class_NonInstantiableClass_0->args[0].value, ZEND_ATTRIBUTE_TARGET_CLASS); + + return class_entry; +} diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 840d2dbe32e1..790335f27a2a 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -20,6 +20,7 @@ #include "zend.h" #include "zend_API.h" #include "zend_closures.h" +#include "zend_attributes.h" #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_objects.h" @@ -448,13 +449,6 @@ ZEND_METHOD(Closure, getCurrent) RETURN_OBJ_COPY(obj); } -static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */ -{ - zend_throw_error(NULL, "Instantiation of class Closure is not allowed"); - return NULL; -} -/* }}} */ - /* int return due to Object Handler API */ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ { @@ -736,7 +730,6 @@ void zend_register_closure_ce(void) /* {{{ */ memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers)); closure_handlers.free_obj = zend_closure_free_storage; - closure_handlers.get_constructor = zend_closure_get_constructor; closure_handlers.get_method = zend_closure_get_method; closure_handlers.compare = zend_closure_compare; closure_handlers.clone_obj = zend_closure_clone; diff --git a/Zend/zend_closures.stub.php b/Zend/zend_closures.stub.php index 46b51617eef9..f44e10527e3c 100644 --- a/Zend/zend_closures.stub.php +++ b/Zend/zend_closures.stub.php @@ -6,6 +6,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Instantiation of class Closure is not allowed")] final class Closure { private function __construct() {} diff --git a/Zend/zend_closures_arginfo.h b/Zend/zend_closures_arginfo.h index 5bc983a97c2c..004430f6fb1b 100644 --- a/Zend/zend_closures_arginfo.h +++ b/Zend/zend_closures_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_closures.stub.php instead. - * Stub hash: e0626e52adb2d38dad1140c1a28cc7774cc84500 */ + * Stub hash: 95468c1d70556f6ba07327ecc64c396ab4be9206 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Closure___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -51,5 +51,13 @@ static zend_class_entry *register_class_Closure(void) INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Closure_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Closure_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Closure_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Closure_0, true); + zend_string *attribute_NonInstantiableClass_class_Closure_0_arg0_str = zend_string_init("Instantiation of class Closure is not allowed", strlen("Instantiation of class Closure is not allowed"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Closure_0->args[0].value, attribute_NonInstantiableClass_class_Closure_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 105f99d24171..bc38c0b8932b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5704,10 +5704,7 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ const zend_function *fbc = NULL; - if (ce - && ce->default_object_handlers->get_constructor == zend_std_get_constructor - && ce->constructor - && is_func_accessible(ce->constructor)) { + if (ce && ce->constructor && is_func_accessible(ce->constructor)) { fbc = ce->constructor; } @@ -9651,6 +9648,14 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top ce->ce_flags |= ZEND_ACC_TOP_LEVEL; } + /* Add a default "pass" constructor if none are defined */ + if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + if (!non_instantiable_class) { + ce->constructor = (zend_function *) &zend_pass_function; + } + } + /* We currently don't early-bind classes that implement interfaces or use traits */ if (!ce->num_interfaces && !ce->num_traits && !ce->num_hooked_prop_variance_checks #ifdef ZEND_OPCACHE_SHM_REATTACHMENT diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0e31332c97f0..d88da25ea288 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -654,6 +654,13 @@ struct _zend_execute_data { zend_array *extra_named_params; }; +/* export zend_pass_function to allow comparisons against it */ +extern ZEND_API const zend_internal_function zend_pass_function; + +static zend_always_inline bool zend_is_pass_function(const zend_function *fn) { + return fn == (const zend_function*) &zend_pass_function; +} + #define ZEND_CALL_HAS_THIS IS_OBJECT_EX /* Top 16 bits of Z_TYPE_INFO(EX(This)) are used as call_info flags */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4253037fda52..24567937bbc7 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -140,7 +140,7 @@ static zend_arg_info zend_pass_function_arg_info[1] = {0}; ZEND_API const zend_internal_function zend_pass_function = { ZEND_INTERNAL_FUNCTION, /* type */ {0, 0, 0}, /* arg_flags */ - 0, /* fn_flags */ + ZEND_ACC_PUBLIC, /* fn_flags */ NULL, /* name */ NULL, /* scope */ NULL, /* prototype */ diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ba48b19bcfe1..7a63b6d90c02 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -57,9 +57,6 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, const char *string_name, bool handle_exceptions); ZEND_API zend_result zend_eval_stringl_ex(const char *str, size_t str_len, zval *retval_ptr, const char *string_name, bool handle_exceptions); -/* export zend_pass_function to allow comparisons against it */ -extern ZEND_API const zend_internal_function zend_pass_function; - ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 52d409f2e69b..cfc3973bdeef 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -18,6 +18,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_attributes.h" #include "zend_hash.h" #include "zend_interfaces.h" #include "zend_exceptions.h" @@ -483,14 +484,6 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ * } /* }}} */ -static ZEND_COLD zend_function *zend_generator_get_constructor(zend_object *object) /* {{{ */ -{ - zend_throw_error(NULL, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated"); - - return NULL; -} -/* }}} */ - ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr) { if (!ptr->func && Z_TYPE(ptr->This) == IS_OBJECT) { @@ -1246,7 +1239,6 @@ void zend_register_generator_ce(void) /* {{{ */ zend_generator_handlers.dtor_obj = zend_generator_dtor_storage; zend_generator_handlers.get_gc = zend_generator_get_gc; zend_generator_handlers.clone_obj = NULL; - zend_generator_handlers.get_constructor = zend_generator_get_constructor; zend_ce_ClosedGeneratorException = register_class_ClosedGeneratorException(zend_ce_exception); } diff --git a/Zend/zend_generators.stub.php b/Zend/zend_generators.stub.php index c081d8e35e26..7d1034221c35 100644 --- a/Zend/zend_generators.stub.php +++ b/Zend/zend_generators.stub.php @@ -6,6 +6,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("The \"Generator\" class is reserved for internal use and cannot be manually instantiated")] final class Generator implements Iterator { public function rewind(): void {} diff --git a/Zend/zend_generators_arginfo.h b/Zend/zend_generators_arginfo.h index 6d8d7989c423..f4a60830054d 100644 --- a/Zend/zend_generators_arginfo.h +++ b/Zend/zend_generators_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_generators.stub.php instead. - * Stub hash: d376e984db0db6ccd9356f632f9d7e1382b2afb7 */ + * Stub hash: 923b63f7385bcb1157ac45d8ff15564e57deb085 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Generator_rewind, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -58,6 +58,14 @@ static zend_class_entry *register_class_Generator(zend_class_entry *class_entry_ class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); zend_class_implements(class_entry, 1, class_entry_Iterator); + zend_string *attribute_name_NonInstantiableClass_class_Generator_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Generator_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Generator_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Generator_0, true); + zend_string *attribute_NonInstantiableClass_class_Generator_0_arg0_str = zend_string_init("The \"Generator\" class is reserved for internal use and cannot be manually instantiated", strlen("The \"Generator\" class is reserved for internal use and cannot be manually instantiated"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Generator_0->args[0].value, attribute_NonInstantiableClass_class_Generator_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index e85b4ea42250..efbc9f857163 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -186,7 +186,7 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ ce->__debugInfo = parent->__debugInfo; } - if (ce->constructor) { + if (ce->constructor && !zend_is_pass_function(ce->constructor)) { if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { zend_error_noreturn(E_ERROR, "Cannot override final %s::__construct() with %s::__construct()", ZSTR_VAL(parent->name), diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 694ef398e374..2c59e3455f19 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -148,4 +148,24 @@ static zend_always_inline bool zend_check_method_accessible(const zend_function return true; } +static inline zend_function *zend_get_public_constructor(const zend_class_entry *ce) { + if (UNEXPECTED(ce->constructor == NULL)) { + return NULL; + } + if (EXPECTED(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { + return ce->constructor; + } + return NULL; +} + +static inline zend_function *zend_get_accessible_constructor_in_scope(const zend_class_entry *ce, const zend_class_entry *scope) { + if (UNEXPECTED(ce->constructor == NULL)) { + return NULL; + } + if (EXPECTED(zend_check_method_accessible(ce->constructor, scope))) { + return ce->constructor; + } + return NULL; +} + #endif /* ZEND_OBJECTS_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 1de7a7cd4195..701140dedbdb 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3847,7 +3847,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, FREE_OP2(); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -6008,7 +6008,18 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -6021,7 +6032,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5b52f1941845..a51c97c2b694 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7628,7 +7628,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -10357,7 +10357,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -11149,7 +11149,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -11401,7 +11401,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_CONS } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -11414,7 +11425,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_CONS /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -12971,7 +12982,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -25603,7 +25614,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -28298,7 +28309,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -29503,7 +29514,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -30077,7 +30088,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_VAR_ } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -30090,7 +30112,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_VAR_ /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -32174,7 +32196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -34386,7 +34408,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -36468,7 +36490,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -36890,7 +36912,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -37122,7 +37144,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUS } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -37135,7 +37168,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUS /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -39053,7 +39086,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -60302,7 +60335,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -63031,7 +63064,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -63721,7 +63754,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -63973,7 +64006,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_CONST_UNU } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -63986,7 +64030,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_CONST_UNU /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -65543,7 +65587,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -78075,7 +78119,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -80770,7 +80814,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -81975,7 +82019,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -82549,7 +82593,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_VAR_UNUSE } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -82562,7 +82617,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_VAR_UNUSE /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -84646,7 +84701,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -86858,7 +86913,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -88940,7 +88995,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -89362,7 +89417,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -89594,7 +89649,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UN } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (constructor == NULL) { + if (UNEXPECTED(constructor == NULL)) { + /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ + if (UNEXPECTED(!EG(exception))) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); + } + HANDLE_EXCEPTION(); + } + /* Pass function is special */ + else if (zend_is_pass_function(constructor)) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -89607,7 +89673,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UN /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, + ZEND_CALL_FUNCTION, constructor, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -91525,7 +91591,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL)) { + if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } diff --git a/build/gen_stub.php b/build/gen_stub.php index 619c4c905b60..2bebd4e0bf37 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3525,10 +3525,14 @@ function (Name $item) { $declaredStrings = []; + $isInstantiable = true; if (!empty($this->attributes)) { $code .= $php80CondStart; foreach ($this->attributes as $key => $attribute) { + if ($attribute->class === "NonInstantiableClass") { + $isInstantiable = false; + } $code .= $attribute->generateCode( "zend_add_class_attribute(class_entry", "class_{$escapedName}_$key", @@ -3559,6 +3563,10 @@ function (Name $item) { $code .= $php80CondEnd; } + if (!$isInstantiable) { + $code .= "\n\tclass_entry->constructor = NULL;\n"; + } + $code .= "\n\treturn class_entry;\n"; $code .= "}\n"; diff --git a/ext/curl/curl.stub.php b/ext/curl/curl.stub.php index aadab8cb0b0d..dafc3461f1c5 100644 --- a/ext/curl/curl.stub.php +++ b/ext/curl/curl.stub.php @@ -3711,6 +3711,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct CurlHandle, use curl_init() instead")] final class CurlHandle { } @@ -3719,6 +3720,7 @@ final class CurlHandle * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct CurlMultiHandle, use curl_multi_init() instead")] final class CurlMultiHandle { } @@ -3727,6 +3729,7 @@ final class CurlMultiHandle * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct CurlShareHandle, use curl_share_init() instead")] final class CurlShareHandle { } @@ -3735,6 +3738,7 @@ final class CurlShareHandle * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead")] final class CurlSharePersistentHandle { public readonly array $options; diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index 6fb17ed029e3..32fd03bdd094 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit curl.stub.php instead. - * Stub hash: 10ebdc94560ed19ecd6b61a11b3dab5d32989d66 */ + * Stub hash: 028c7c273b18c285ba9fa090dcd030f7dd48148f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -1014,6 +1014,14 @@ static zend_class_entry *register_class_CurlHandle(void) INIT_CLASS_ENTRY(ce, "CurlHandle", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_CurlHandle_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_CurlHandle_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_CurlHandle_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_CurlHandle_0, true); + zend_string *attribute_NonInstantiableClass_class_CurlHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlHandle, use curl_init() instead", strlen("Cannot directly construct CurlHandle, use curl_init() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_CurlHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlHandle_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1024,6 +1032,14 @@ static zend_class_entry *register_class_CurlMultiHandle(void) INIT_CLASS_ENTRY(ce, "CurlMultiHandle", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_CurlMultiHandle_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_CurlMultiHandle_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_CurlMultiHandle_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_CurlMultiHandle_0, true); + zend_string *attribute_NonInstantiableClass_class_CurlMultiHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlMultiHandle, use curl_multi_init() instead", strlen("Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_CurlMultiHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlMultiHandle_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1034,6 +1050,14 @@ static zend_class_entry *register_class_CurlShareHandle(void) INIT_CLASS_ENTRY(ce, "CurlShareHandle", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_CurlShareHandle_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_CurlShareHandle_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_CurlShareHandle_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_CurlShareHandle_0, true); + zend_string *attribute_NonInstantiableClass_class_CurlShareHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlShareHandle, use curl_share_init() instead", strlen("Cannot directly construct CurlShareHandle, use curl_share_init() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_CurlShareHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlShareHandle_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1050,5 +1074,13 @@ static zend_class_entry *register_class_CurlSharePersistentHandle(void) zend_declare_typed_property(class_entry, property_options_name, &property_options_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); zend_string_release_ex(property_options_name, true); + zend_string *attribute_name_NonInstantiableClass_class_CurlSharePersistentHandle_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_CurlSharePersistentHandle_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_CurlSharePersistentHandle_0, true); + zend_string *attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead", strlen("Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/curl/interface.c b/ext/curl/interface.c index ed544866a886..9ecc1f299997 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -214,7 +214,6 @@ static zend_object_handlers curl_object_handlers; static zend_object *curl_create_object(zend_class_entry *class_type); static void curl_free_obj(zend_object *object); static HashTable *curl_get_gc(zend_object *object, zval **table, int *n); -static zend_function *curl_get_constructor(zend_object *object); static zend_object *curl_clone_obj(zend_object *object); php_curl *init_curl_handle_into_zval(zval *curl); static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); @@ -360,7 +359,6 @@ PHP_MINIT_FUNCTION(curl) curl_object_handlers.offset = offsetof(php_curl, std); curl_object_handlers.free_obj = curl_free_obj; curl_object_handlers.get_gc = curl_get_gc; - curl_object_handlers.get_constructor = curl_get_constructor; curl_object_handlers.clone_obj = curl_clone_obj; curl_object_handlers.cast_object = curl_cast_object; curl_object_handlers.compare = zend_objects_not_comparable; @@ -391,11 +389,6 @@ static zend_object *curl_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *curl_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); - return NULL; -} - static zend_object *curl_clone_obj(zend_object *object) { php_curl *ch; CURL *cp; diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 36ea47a8bca2..f745cbdf721b 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -20,6 +20,7 @@ #include "php.h" #include "Zend/zend_smart_str.h" +#include "zend_attributes.h" #include "curl_private.h" @@ -538,11 +539,6 @@ static zend_object *curl_multi_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *curl_multi_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); - return NULL; -} - static void curl_multi_free_obj(zend_object *object) { php_curlm *mh = curl_multi_from_obj(object); @@ -608,7 +604,6 @@ void curl_multi_register_handlers(void) { curl_multi_handlers.offset = offsetof(php_curlm, std); curl_multi_handlers.free_obj = curl_multi_free_obj; curl_multi_handlers.get_gc = curl_multi_get_gc; - curl_multi_handlers.get_constructor = curl_multi_get_constructor; curl_multi_handlers.clone_obj = NULL; curl_multi_handlers.cast_object = curl_cast_object; curl_multi_handlers.compare = zend_objects_not_comparable; diff --git a/ext/curl/share.c b/ext/curl/share.c index 56cd804658a4..b05c2807e8a7 100644 --- a/ext/curl/share.c +++ b/ext/curl/share.c @@ -20,6 +20,7 @@ #include "php.h" #include "Zend/zend_exceptions.h" +#include "zend_attributes.h" #include "curl_private.h" @@ -283,11 +284,6 @@ static zend_object *curl_share_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *curl_share_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlShareHandle, use curl_share_init() instead"); - return NULL; -} - void curl_share_free_obj(zend_object *object) { php_curlsh *sh = curl_share_from_obj(object); @@ -305,7 +301,6 @@ void curl_share_register_handlers(void) { memcpy(&curl_share_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_share_handlers.offset = offsetof(php_curlsh, std); curl_share_handlers.free_obj = curl_share_free_obj; - curl_share_handlers.get_constructor = curl_share_get_constructor; curl_share_handlers.clone_obj = NULL; curl_share_handlers.compare = zend_objects_not_comparable; } @@ -314,18 +309,12 @@ void curl_share_register_handlers(void) { static zend_object_handlers curl_share_persistent_handlers; -static zend_function *curl_share_persistent_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead"); - return NULL; -} - void curl_share_persistent_register_handlers(void) { curl_share_persistent_ce->create_object = curl_share_create_object; curl_share_persistent_ce->default_object_handlers = &curl_share_persistent_handlers; memcpy(&curl_share_persistent_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_share_persistent_handlers.offset = offsetof(php_curlsh, std); - curl_share_persistent_handlers.get_constructor = curl_share_persistent_get_constructor; curl_share_persistent_handlers.clone_obj = NULL; curl_share_persistent_handlers.compare = zend_objects_not_comparable; } diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 29c560973b77..31811f91b9b8 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -31,6 +31,7 @@ #include "php_dba.h" #include "ext/standard/info.h" #include "ext/standard/flock_compat.h" /* Compatibility for Windows */ +#include "zend_attributes.h" #include "php_gdbm.h" #include "php_ndbm.h" @@ -316,12 +317,6 @@ static zend_object *dba_connection_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *dba_connection_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead"); - return NULL; -} - static zend_result dba_connection_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { @@ -411,7 +406,6 @@ PHP_MINIT_FUNCTION(dba) memcpy(&dba_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); dba_connection_object_handlers.offset = offsetof(dba_connection, std); dba_connection_object_handlers.free_obj = dba_connection_free_obj; - dba_connection_object_handlers.get_constructor = dba_connection_get_constructor; dba_connection_object_handlers.clone_obj = NULL; dba_connection_object_handlers.cast_object = dba_connection_cast_object; dba_connection_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/dba/dba.stub.php b/ext/dba/dba.stub.php index ab9758e2c2bb..3d3c01c72192 100644 --- a/ext/dba/dba.stub.php +++ b/ext/dba/dba.stub.php @@ -7,6 +7,7 @@ * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead")] final class Connection { } diff --git a/ext/dba/dba_arginfo.h b/ext/dba/dba_arginfo.h index 22978b68fc7b..31c3c994d5d3 100644 --- a/ext/dba/dba_arginfo.h +++ b/ext/dba/dba_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit dba.stub.php instead. - * Stub hash: d7ff53b73d3921c41ffd8279ea724bcd3a6d8542 */ + * Stub hash: 6ba636a9b106a1ec8c4332db15f1060efafb886e */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dba_popen, 0, 2, Dba\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) @@ -110,5 +110,13 @@ static zend_class_entry *register_class_Dba_Connection(void) INIT_NS_CLASS_ENTRY(ce, "Dba", "Connection", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Dba_Connection_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Dba_Connection_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Dba_Connection_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Dba_Connection_0, true); + zend_string *attribute_NonInstantiableClass_class_Dba_Connection_0_arg0_str = zend_string_init("Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead", strlen("Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Dba_Connection_0->args[0].value, attribute_NonInstantiableClass_class_Dba_Connection_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 779b41fcad3d..7eb9580d043b 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -20,6 +20,7 @@ #include "php_ffi.h" #include "ext/standard/info.h" #include "php_scandir.h" +#include "zend_attributes.h" #include "zend_exceptions.h" #include "zend_closures.h" #include "zend_weakrefs.h" @@ -5123,13 +5124,6 @@ static char *zend_ffi_parse_directives(const char *filename, char *code_pos, cha } /* }}} */ -static ZEND_COLD zend_function *zend_fake_get_constructor(zend_object *object) /* {{{ */ -{ - zend_throw_error(NULL, "Instantiation of %s is not allowed", ZSTR_VAL(object->ce->name)); - return NULL; -} -/* }}} */ - static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry *ce) /* {{{ */ { zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name)); @@ -5482,7 +5476,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_post_startup_cb = ffi_fixup_temporaries; memcpy(&zend_ffi_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_ffi_handlers.get_constructor = zend_fake_get_constructor; zend_ffi_handlers.free_obj = zend_ffi_free_obj; zend_ffi_handlers.clone_obj = NULL; zend_ffi_handlers.read_property = zend_ffi_read_var; @@ -5508,7 +5501,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_cdata_ce->get_iterator = zend_ffi_cdata_get_iterator; memcpy(&zend_ffi_cdata_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_ffi_cdata_handlers.get_constructor = zend_fake_get_constructor; zend_ffi_cdata_handlers.free_obj = zend_ffi_cdata_free_obj; zend_ffi_cdata_handlers.clone_obj = zend_ffi_cdata_clone_obj; zend_ffi_cdata_handlers.read_property = zend_ffi_cdata_read_field; @@ -5532,7 +5524,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_cdata_handlers.get_gc = zend_fake_get_gc; memcpy(&zend_ffi_cdata_value_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_ffi_cdata_value_handlers.get_constructor = zend_fake_get_constructor; zend_ffi_cdata_value_handlers.free_obj = zend_ffi_cdata_free_obj; zend_ffi_cdata_value_handlers.clone_obj = zend_ffi_cdata_clone_obj; zend_ffi_cdata_value_handlers.read_property = zend_ffi_cdata_get; @@ -5555,7 +5546,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_cdata_value_handlers.get_gc = zend_fake_get_gc; memcpy(&zend_ffi_cdata_free_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_ffi_cdata_free_handlers.get_constructor = zend_fake_get_constructor; zend_ffi_cdata_free_handlers.free_obj = zend_ffi_cdata_free_obj; zend_ffi_cdata_free_handlers.clone_obj = zend_ffi_free_clone_obj; zend_ffi_cdata_free_handlers.read_property = zend_ffi_free_read_property; @@ -5582,7 +5572,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_ctype_ce->default_object_handlers = &zend_ffi_ctype_handlers; memcpy(&zend_ffi_ctype_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_ffi_ctype_handlers.get_constructor = zend_fake_get_constructor; zend_ffi_ctype_handlers.free_obj = zend_ffi_ctype_free_obj; zend_ffi_ctype_handlers.clone_obj = NULL; zend_ffi_ctype_handlers.read_property = zend_fake_read_property; diff --git a/ext/ffi/ffi.stub.php b/ext/ffi/ffi.stub.php index 3fb9ceee3dfa..07e6cd8d5721 100644 --- a/ext/ffi/ffi.stub.php +++ b/ext/ffi/ffi.stub.php @@ -4,6 +4,7 @@ namespace { /** @not-serializable */ + #[\NonInstantiableClass("Instantiation of FFI is not allowed")] final class FFI { /** @cvalue __BIGGEST_ALIGNMENT__ */ @@ -72,10 +73,12 @@ public static function isNull(FFI\CData $ptr): bool {} namespace FFI { /** @not-serializable */ + #[\NonInstantiableClass("Instantiation of FFI\\CData is not allowed")] final class CData { } /** @not-serializable */ + #[\NonInstantiableClass("Instantiation of FFI\\CType is not allowed")] final class CType { /** @cvalue ZEND_FFI_TYPE_VOID */ public const int TYPE_VOID = UNKNOWN; diff --git a/ext/ffi/ffi_arginfo.h b/ext/ffi/ffi_arginfo.h index a263e0bfb32e..82f74a51cbca 100644 --- a/ext/ffi/ffi_arginfo.h +++ b/ext/ffi/ffi_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit ffi.stub.php instead. - * Stub hash: d3626f5d39317876fc7d4f240b0758f17f3472c8 */ + * Stub hash: 52f9aa63a3b3f307e8d83640e527fe61775799c8 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_cdef, 0, 0, FFI, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_STRING, 0, "\"\"") @@ -209,6 +209,14 @@ static zend_class_entry *register_class_FFI(void) zend_declare_typed_class_constant(class_entry, const___BIGGEST_ALIGNMENT___name, &const___BIGGEST_ALIGNMENT___value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const___BIGGEST_ALIGNMENT___name, true); + zend_string *attribute_name_NonInstantiableClass_class_FFI_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_FFI_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_FFI_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_FFI_0, true); + zend_string *attribute_NonInstantiableClass_class_FFI_0_arg0_str = zend_string_init("Instantiation of FFI is not allowed", strlen("Instantiation of FFI is not allowed"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_0->args[0].value, attribute_NonInstantiableClass_class_FFI_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -219,6 +227,14 @@ static zend_class_entry *register_class_FFI_CData(void) INIT_NS_CLASS_ENTRY(ce, "FFI", "CData", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_FFI_CData_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_FFI_CData_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_FFI_CData_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_FFI_CData_0, true); + zend_string *attribute_NonInstantiableClass_class_FFI_CData_0_arg0_str = zend_string_init("Instantiation of FFI\\CData is not allowed", strlen("Instantiation of FFI\\CData is not allowed"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_CData_0->args[0].value, attribute_NonInstantiableClass_class_FFI_CData_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -459,6 +475,14 @@ static zend_class_entry *register_class_FFI_CType(void) zend_declare_typed_class_constant(class_entry, const_ABI_VECTORCALL_name, &const_ABI_VECTORCALL_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const_ABI_VECTORCALL_name, true); + zend_string *attribute_name_NonInstantiableClass_class_FFI_CType_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_FFI_CType_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_FFI_CType_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_FFI_CType_0, true); + zend_string *attribute_NonInstantiableClass_class_FFI_CType_0_arg0_str = zend_string_init("Instantiation of FFI\\CType is not allowed", strlen("Instantiation of FFI\\CType is not allowed"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_CType_0->args[0].value, attribute_NonInstantiableClass_class_FFI_CType_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/ftp/ftp.stub.php b/ext/ftp/ftp.stub.php index 6560dd893018..31e2b1593e44 100644 --- a/ext/ftp/ftp.stub.php +++ b/ext/ftp/ftp.stub.php @@ -141,6 +141,7 @@ function ftp_get_option(FTP\Connection $ftp, int $option): int|bool {} * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead")] final class Connection { } diff --git a/ext/ftp/ftp_arginfo.h b/ext/ftp/ftp_arginfo.h index edb0b4b8a91b..b35cb83254a3 100644 --- a/ext/ftp/ftp_arginfo.h +++ b/ext/ftp/ftp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit ftp.stub.php instead. - * Stub hash: 29606d7114a0698b8ae231173a624b17c196ffec */ + * Stub hash: 42ce274f711a6db22d57cf8b1c689846fcb21118 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ftp_connect, 0, 1, FTP\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0) @@ -298,5 +298,13 @@ static zend_class_entry *register_class_FTP_Connection(void) INIT_NS_CLASS_ENTRY(ce, "FTP", "Connection", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_FTP_Connection_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_FTP_Connection_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_FTP_Connection_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_FTP_Connection_0, true); + zend_string *attribute_NonInstantiableClass_class_FTP_Connection_0_arg0_str = zend_string_init("Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead", strlen("Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_FTP_Connection_0->args[0].value, attribute_NonInstantiableClass_class_FTP_Connection_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 501ce9bc68d7..e2ccd1617d55 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -78,11 +78,6 @@ static zend_object* ftp_object_create(zend_class_entry* ce) { return zobj; } -static zend_function *ftp_object_get_constructor(zend_object *zobj) { - zend_throw_error(NULL, "Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead"); - return NULL; -} - static void ftp_object_destroy(zend_object *zobj) { php_ftp_object *obj = ftp_object_from_zend_object(zobj); @@ -100,7 +95,6 @@ PHP_MINIT_FUNCTION(ftp) memcpy(&ftp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); ftp_object_handlers.offset = offsetof(php_ftp_object, std); - ftp_object_handlers.get_constructor = ftp_object_get_constructor; ftp_object_handlers.free_obj = ftp_object_destroy; ftp_object_handlers.clone_obj = NULL; diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 92ab74fda575..7d935c89e6c1 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -138,12 +138,6 @@ typedef struct _gd_ext_image_object { static zend_object_handlers php_gd_image_object_handlers; -static zend_function *php_gd_image_object_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "You cannot initialize a GdImage object except through helper functions"); - return NULL; -} - /** * Returns the underlying php_gd_image_object from a zend_object */ @@ -204,7 +198,6 @@ static void php_gd_object_minit_helper(void) memcpy(&php_gd_image_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_gd_image_object_handlers.clone_obj = NULL; php_gd_image_object_handlers.free_obj = php_gd_image_object_free; - php_gd_image_object_handlers.get_constructor = php_gd_image_object_get_constructor; php_gd_image_object_handlers.compare = zend_objects_not_comparable; php_gd_image_object_handlers.offset = offsetof(php_gd_image_object, std); } @@ -255,12 +248,6 @@ static void php_gd_font_object_free(zend_object *zobj) zend_object_std_dtor(zobj); } -static zend_function *php_gd_font_object_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "You cannot initialize a GdFont object except through helper functions"); - return NULL; -} - static void php_gd_font_minit_helper(void) { gd_font_ce = register_class_GdFont(); @@ -270,7 +257,6 @@ static void php_gd_font_minit_helper(void) memcpy(&php_gd_font_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_gd_font_object_handlers.clone_obj = NULL; php_gd_font_object_handlers.free_obj = php_gd_font_object_free; - php_gd_font_object_handlers.get_constructor = php_gd_font_object_get_constructor; php_gd_font_object_handlers.offset = offsetof(php_gd_font_object, std); } diff --git a/ext/gd/gd.stub.php b/ext/gd/gd.stub.php index 0632724b44f3..83fd850872ef 100644 --- a/ext/gd/gd.stub.php +++ b/ext/gd/gd.stub.php @@ -463,12 +463,14 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct GdImage, use an appropriate image* function instead")] final class GdImage {} /** * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct GdFont, use imageloadfont() instead")] final class GdFont {} /** diff --git a/ext/gd/gd_arginfo.h b/ext/gd/gd_arginfo.h index 6b6327fd682f..77006fe1574b 100644 --- a/ext/gd/gd_arginfo.h +++ b/ext/gd/gd_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit gd.stub.php instead. - * Stub hash: 2cdc0b485d9b62bb9021973d3c8cce0169b21ac0 */ + * Stub hash: 60466cdb1b67fe1d70000f0e5b0a6831f7be973e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_gd_info, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -940,6 +940,14 @@ static zend_class_entry *register_class_GdImage(void) INIT_CLASS_ENTRY(ce, "GdImage", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_GdImage_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_GdImage_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_GdImage_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_GdImage_0, true); + zend_string *attribute_NonInstantiableClass_class_GdImage_0_arg0_str = zend_string_init("Cannot directly construct GdImage, use an appropriate image* function instead", strlen("Cannot directly construct GdImage, use an appropriate image* function instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_GdImage_0->args[0].value, attribute_NonInstantiableClass_class_GdImage_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -950,5 +958,13 @@ static zend_class_entry *register_class_GdFont(void) INIT_CLASS_ENTRY(ce, "GdFont", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_GdFont_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_GdFont_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_GdFont_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_GdFont_0, true); + zend_string *attribute_NonInstantiableClass_class_GdFont_0_arg0_str = zend_string_init("Cannot directly construct GdFont, use imageloadfont() instead", strlen("Cannot directly construct GdFont, use imageloadfont() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_GdFont_0->args[0].value, attribute_NonInstantiableClass_class_GdFont_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 77322d2de59f..86c98fb93b83 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -115,11 +115,6 @@ static zend_object *ldap_link_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *ldap_link_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct LDAP\\Connection, use ldap_connect() instead"); - return NULL; -} - static void ldap_link_free(ldap_linkdata *ld) { /* We use ldap_destroy rather than ldap_unbind here, because ldap_unbind @@ -162,11 +157,6 @@ static zend_object *ldap_result_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *ldap_result_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct LDAP\\Result, use the dedicated functions instead"); - return NULL; -} - static void ldap_result_free(ldap_resultdata *result) { ldap_msgfree(result->result); @@ -199,11 +189,6 @@ static zend_object *ldap_result_entry_create_object(zend_class_entry *class_type return &intern->std; } -static zend_function *ldap_result_entry_get_constructor(zend_object *obj) { - zend_throw_error(NULL, "Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead"); - return NULL; -} - static void ldap_result_entry_free_obj(zend_object *obj) { ldap_result_entry *entry = ldap_result_entry_from_obj(obj); @@ -879,7 +864,6 @@ PHP_MINIT_FUNCTION(ldap) memcpy(&ldap_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); ldap_link_object_handlers.offset = offsetof(ldap_linkdata, std); ldap_link_object_handlers.free_obj = ldap_link_free_obj; - ldap_link_object_handlers.get_constructor = ldap_link_get_constructor; ldap_link_object_handlers.clone_obj = NULL; ldap_link_object_handlers.compare = zend_objects_not_comparable; @@ -890,7 +874,6 @@ PHP_MINIT_FUNCTION(ldap) memcpy(&ldap_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); ldap_result_object_handlers.offset = offsetof(ldap_resultdata, std); ldap_result_object_handlers.free_obj = ldap_result_free_obj; - ldap_result_object_handlers.get_constructor = ldap_result_get_constructor; ldap_result_object_handlers.clone_obj = NULL; ldap_result_object_handlers.compare = zend_objects_not_comparable; @@ -901,7 +884,6 @@ PHP_MINIT_FUNCTION(ldap) memcpy(&ldap_result_entry_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); ldap_result_entry_object_handlers.offset = offsetof(ldap_result_entry, std); ldap_result_entry_object_handlers.free_obj = ldap_result_entry_free_obj; - ldap_result_entry_object_handlers.get_constructor = ldap_result_entry_get_constructor; ldap_result_entry_object_handlers.clone_obj = NULL; ldap_result_entry_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/ldap/ldap.stub.php b/ext/ldap/ldap.stub.php index 52cf3828dba3..5a11c9c28139 100644 --- a/ext/ldap/ldap.stub.php +++ b/ext/ldap/ldap.stub.php @@ -833,6 +833,7 @@ function ldap_parse_exop(LDAP\Connection $ldap, LDAP\Result $result, &$response_ * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct LDAP\\Connection, use ldap_connect() instead")] final class Connection { } @@ -841,6 +842,7 @@ final class Connection * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct LDAP\\Result, use the dedicated functions instead")] final class Result { } @@ -849,6 +851,7 @@ final class Result * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead")] final class ResultEntry { } diff --git a/ext/ldap/ldap_arginfo.h b/ext/ldap/ldap_arginfo.h index 8f5e7e34ba32..2728c68e8a06 100644 --- a/ext/ldap/ldap_arginfo.h +++ b/ext/ldap/ldap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit ldap.stub.php instead. - * Stub hash: 0dde8fd813f43640dee842c03365d7431858a56d */ + * Stub hash: 908a50f948c3c80ab77a2a6b1541bb533993c1d7 */ #if defined(HAVE_ORALDAP) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ldap_connect, 0, 0, LDAP\\Connection, MAY_BE_FALSE) @@ -775,6 +775,14 @@ static zend_class_entry *register_class_LDAP_Connection(void) INIT_NS_CLASS_ENTRY(ce, "LDAP", "Connection", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_LDAP_Connection_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_LDAP_Connection_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_LDAP_Connection_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_LDAP_Connection_0, true); + zend_string *attribute_NonInstantiableClass_class_LDAP_Connection_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\Connection, use ldap_connect() instead", strlen("Cannot directly construct LDAP\\Connection, use ldap_connect() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_Connection_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_Connection_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -785,6 +793,14 @@ static zend_class_entry *register_class_LDAP_Result(void) INIT_NS_CLASS_ENTRY(ce, "LDAP", "Result", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_LDAP_Result_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_LDAP_Result_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_LDAP_Result_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_LDAP_Result_0, true); + zend_string *attribute_NonInstantiableClass_class_LDAP_Result_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\Result, use the dedicated functions instead", strlen("Cannot directly construct LDAP\\Result, use the dedicated functions instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_Result_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_Result_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -795,5 +811,13 @@ static zend_class_entry *register_class_LDAP_ResultEntry(void) INIT_NS_CLASS_ENTRY(ce, "LDAP", "ResultEntry", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_LDAP_ResultEntry_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_LDAP_ResultEntry_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_LDAP_ResultEntry_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_LDAP_ResultEntry_0, true); + zend_string *attribute_NonInstantiableClass_class_LDAP_ResultEntry_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead", strlen("Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_ResultEntry_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_ResultEntry_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index b45e7416c773..3826584f5218 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include "../../Zend/zend_compile.h" #ifdef HAVE_CONFIG_H #include #endif @@ -760,10 +761,18 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags if (ce == NULL) { ce = zend_standard_class_def; } - if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { + /* TODO: should we only allow public constructors? */ + if (UNEXPECTED(ce->constructor == NULL || ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { zend_throw_error(NULL, "Class %s cannot be instantiated", ZSTR_VAL(ce->name)); RETURN_THROWS(); } + if (zend_is_pass_function(ce->constructor) && ctor_params && zend_hash_num_elements(ctor_params) > 0) { + zend_argument_value_error(ERROR_ARG_POS(3), + "must be empty when the specified class (%s) does not have a constructor", + ZSTR_VAL(ce->name) + ); + RETURN_THROWS(); + } fetchtype = MYSQLI_ASSOC; } else { if (override_flags) { @@ -802,14 +811,9 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags zend_array_release(prop_table); } - if (ce->constructor) { + if (!zend_is_pass_function(ce->constructor)) { zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); - } else if (ctor_params && zend_hash_num_elements(ctor_params) > 0) { - zend_argument_value_error(ERROR_ARG_POS(3), - "must be empty when the specified class (%s) does not have a constructor", - ZSTR_VAL(ce->name) - ); } } } diff --git a/ext/odbc/odbc.stub.php b/ext/odbc/odbc.stub.php index f88a71154f8f..062f823a41d2 100644 --- a/ext/odbc/odbc.stub.php +++ b/ext/odbc/odbc.stub.php @@ -7,6 +7,7 @@ * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead")] class Connection { } @@ -15,6 +16,7 @@ class Connection * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead")] class Result { } diff --git a/ext/odbc/odbc_arginfo.h b/ext/odbc/odbc_arginfo.h index badd1400148d..5e94bc33abd9 100644 --- a/ext/odbc/odbc_arginfo.h +++ b/ext/odbc/odbc_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit odbc.stub.php instead. - * Stub hash: f9ba28767b256dbcea087a65aa4bb5f5b509d6f3 */ + * Stub hash: 7b35a7bbf36fdec269eebb54afe46bdfd6a0cc3b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_close_all, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -408,6 +408,14 @@ static zend_class_entry *register_class_Odbc_Connection(void) INIT_NS_CLASS_ENTRY(ce, "Odbc", "Connection", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Odbc_Connection_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Odbc_Connection_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Odbc_Connection_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Odbc_Connection_0, true); + zend_string *attribute_NonInstantiableClass_class_Odbc_Connection_0_arg0_str = zend_string_init("Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead", strlen("Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Odbc_Connection_0->args[0].value, attribute_NonInstantiableClass_class_Odbc_Connection_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -418,5 +426,13 @@ static zend_class_entry *register_class_Odbc_Result(void) INIT_NS_CLASS_ENTRY(ce, "Odbc", "Result", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Odbc_Result_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Odbc_Result_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Odbc_Result_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Odbc_Result_0, true); + zend_string *attribute_NonInstantiableClass_class_Odbc_Result_0_arg0_str = zend_string_init("Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead", strlen("Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Odbc_Result_0->args[0].value, attribute_NonInstantiableClass_class_Odbc_Result_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 45d30f13a56f..a67d916148e0 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -174,12 +174,6 @@ static zend_object *odbc_connection_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *odbc_connection_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead"); - return NULL; -} - static zend_result odbc_connection_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { @@ -217,12 +211,6 @@ static zend_object *odbc_result_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *odbc_result_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead"); - return NULL; -} - static zend_result odbc_result_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { @@ -509,7 +497,6 @@ PHP_MINIT_FUNCTION(odbc) memcpy(&odbc_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); odbc_connection_object_handlers.offset = offsetof(odbc_link, std); odbc_connection_object_handlers.free_obj = odbc_connection_free_obj; - odbc_connection_object_handlers.get_constructor = odbc_connection_get_constructor; odbc_connection_object_handlers.clone_obj = NULL; odbc_connection_object_handlers.cast_object = odbc_connection_cast_object; odbc_connection_object_handlers.compare = zend_objects_not_comparable; @@ -521,7 +508,6 @@ PHP_MINIT_FUNCTION(odbc) memcpy(&odbc_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); odbc_result_object_handlers.offset = offsetof(odbc_result, std); odbc_result_object_handlers.free_obj = odbc_result_free_obj; - odbc_result_object_handlers.get_constructor = odbc_result_get_constructor; odbc_result_object_handlers.clone_obj = NULL; odbc_result_object_handlers.cast_object = odbc_result_cast_object; odbc_result_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index af59b9b2c34a..6acc8bc97621 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -929,7 +929,9 @@ static void zend_file_cache_serialize_class(zval *zv, } } - SERIALIZE_PTR(ce->constructor); + if (ce->constructor && !zend_is_pass_function(ce->constructor)) { + SERIALIZE_PTR(ce->constructor); + } SERIALIZE_PTR(ce->destructor); SERIALIZE_PTR(ce->clone); SERIALIZE_PTR(ce->__get); @@ -1805,6 +1807,12 @@ static void zend_file_cache_unserialize_class(zval *zv, } UNSERIALIZE_PTR(ce->constructor); + if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) { + zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + if (!non_instantiable_class) { + ce->constructor = (zend_function *) &zend_pass_function; + } + } UNSERIALIZE_PTR(ce->destructor); UNSERIALIZE_PTR(ce->clone); UNSERIALIZE_PTR(ce->__get); diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 65e63265dfc6..2114b15f4224 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -80,11 +80,6 @@ static zend_object *php_openssl_certificate_create_object(zend_class_entry *clas return &intern->std; } -static zend_function *php_openssl_certificate_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead"); - return NULL; -} - static void php_openssl_certificate_free_obj(zend_object *object) { php_openssl_certificate_object *x509_object = php_openssl_certificate_from_obj(object); @@ -113,11 +108,6 @@ static zend_object *php_openssl_request_create_object(zend_class_entry *class_ty return &intern->std; } -static zend_function *php_openssl_request_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead"); - return NULL; -} - static void php_openssl_request_free_obj(zend_object *object) { php_openssl_request_object *x509_request = php_openssl_request_from_obj(object); @@ -155,12 +145,6 @@ static zend_object *php_openssl_pkey_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *php_openssl_pkey_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead"); - return NULL; -} - static void php_openssl_pkey_free_obj(zend_object *object) { php_openssl_pkey_object *key_object = php_openssl_pkey_from_obj(object); @@ -279,13 +263,6 @@ static zend_object *php_openssl_session_create_object(zend_class_entry *class_ty return &intern->std; } -static zend_function *php_openssl_session_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, - "Cannot directly construct OpenSSLSession, use OpenSSLSession::import() or TLS session callbacks"); - return NULL; -} - static void php_openssl_session_free_obj(zend_object *object) { php_openssl_session_object *session_object = php_openssl_session_from_obj(object); @@ -759,7 +736,6 @@ PHP_MINIT_FUNCTION(openssl) memcpy(&php_openssl_certificate_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_openssl_certificate_object_handlers.offset = offsetof(php_openssl_certificate_object, std); php_openssl_certificate_object_handlers.free_obj = php_openssl_certificate_free_obj; - php_openssl_certificate_object_handlers.get_constructor = php_openssl_certificate_get_constructor; php_openssl_certificate_object_handlers.clone_obj = NULL; php_openssl_certificate_object_handlers.compare = zend_objects_not_comparable; @@ -770,7 +746,6 @@ PHP_MINIT_FUNCTION(openssl) memcpy(&php_openssl_request_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_openssl_request_object_handlers.offset = offsetof(php_openssl_request_object, std); php_openssl_request_object_handlers.free_obj = php_openssl_request_free_obj; - php_openssl_request_object_handlers.get_constructor = php_openssl_request_get_constructor; php_openssl_request_object_handlers.clone_obj = NULL; php_openssl_request_object_handlers.compare = zend_objects_not_comparable; @@ -781,7 +756,6 @@ PHP_MINIT_FUNCTION(openssl) memcpy(&php_openssl_pkey_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_openssl_pkey_object_handlers.offset = offsetof(php_openssl_pkey_object, std); php_openssl_pkey_object_handlers.free_obj = php_openssl_pkey_free_obj; - php_openssl_pkey_object_handlers.get_constructor = php_openssl_pkey_get_constructor; php_openssl_pkey_object_handlers.clone_obj = NULL; php_openssl_pkey_object_handlers.compare = zend_objects_not_comparable; @@ -797,7 +771,6 @@ PHP_MINIT_FUNCTION(openssl) memcpy(&php_openssl_session_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_openssl_session_object_handlers.offset = offsetof(php_openssl_session_object, std); php_openssl_session_object_handlers.free_obj = php_openssl_session_free_obj; - php_openssl_session_object_handlers.get_constructor = php_openssl_session_get_constructor; php_openssl_session_object_handlers.clone_obj = NULL; php_openssl_session_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 6080ac323903..abb230818e41 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -32,6 +32,7 @@ public function __construct(string $psk, ?string $identity = null) {} /** * @strict-properties */ + #[\NonInstantiableClass("Cannot directly construct Openssl\\Session, use Openssl\\Session::import() or TLS session callbacks")] final class Session { public readonly string $id; @@ -474,6 +475,7 @@ public function __unserialize(array $data): void {} * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead")] final class OpenSSLCertificate { } @@ -482,6 +484,7 @@ final class OpenSSLCertificate * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead")] final class OpenSSLCertificateSigningRequest { } @@ -490,6 +493,7 @@ final class OpenSSLCertificateSigningRequest * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead")] final class OpenSSLAsymmetricKey { } diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index caf47a256e78..872186ad502c 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit openssl.stub.php instead. - * Stub hash: 4d38e81a2f73bb6dd4bbe7a3e0b8ba86600654e2 */ + * Stub hash: 0058ec2ddd8cd5ce2229ef7cec6a72264a63ed54 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -878,6 +878,14 @@ static zend_class_entry *register_class_Openssl_Session(void) zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_id_name, true); + zend_string *attribute_name_NonInstantiableClass_class_Openssl_Session_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Openssl_Session_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Openssl_Session_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Openssl_Session_0, true); + zend_string *attribute_NonInstantiableClass_class_Openssl_Session_0_arg0_str = zend_string_init("Cannot directly construct Openssl\\Session, use Openssl\\Session::import() or TLS session callbacks", strlen("Cannot directly construct Openssl\\Session, use Openssl\\Session::import() or TLS session callbacks"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Openssl_Session_0->args[0].value, attribute_NonInstantiableClass_class_Openssl_Session_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -888,6 +896,14 @@ static zend_class_entry *register_class_OpenSSLCertificate(void) INIT_CLASS_ENTRY(ce, "OpenSSLCertificate", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_OpenSSLCertificate_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_OpenSSLCertificate_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_OpenSSLCertificate_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_OpenSSLCertificate_0, true); + zend_string *attribute_NonInstantiableClass_class_OpenSSLCertificate_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead", strlen("Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLCertificate_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLCertificate_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -898,6 +914,14 @@ static zend_class_entry *register_class_OpenSSLCertificateSigningRequest(void) INIT_CLASS_ENTRY(ce, "OpenSSLCertificateSigningRequest", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0, true); + zend_string *attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead", strlen("Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -908,5 +932,13 @@ static zend_class_entry *register_class_OpenSSLAsymmetricKey(void) INIT_CLASS_ENTRY(ce, "OpenSSLAsymmetricKey", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_OpenSSLAsymmetricKey_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_OpenSSLAsymmetricKey_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_OpenSSLAsymmetricKey_0, true); + zend_string *attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead", strlen("Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 6a47ec30c862..7669134aeee2 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -626,7 +626,15 @@ PHP_METHOD(PDO, prepare) zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); RETURN_THROWS(); } - if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { + if (UNEXPECTED(dbstmt_ce->constructor == NULL)) { + zend_throw_error( + NULL, + "Class %s cannot be used as a user-supplied statement class as it cannot be instantiated", + ZSTR_VAL(dbstmt_ce->name)); + RETURN_THROWS(); + } + /* Ignore default constructor as it will always be public */ + if (!zend_is_pass_function(dbstmt_ce->constructor) && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { zend_type_error("User-supplied statement class cannot have a public constructor"); RETURN_THROWS(); } @@ -922,7 +930,8 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value, u zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); return false; } - if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { + /* Ignore default constructor as it will always be public */ + if (pce->constructor && !zend_is_pass_function(pce->constructor) && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { zend_argument_type_error(value_arg_num, "User-supplied statement class cannot have a public constructor"); return false; } diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 2b4e5a8f8239..d83ee24e8795 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -27,6 +27,7 @@ #include "php_pdo.h" #include "php_pdo_driver.h" #include "php_pdo_int.h" +#include "zend_attributes.h" #include "zend_exceptions.h" #include "zend_interfaces.h" #include "php_memory_streams.h" @@ -1087,7 +1088,12 @@ PHP_METHOD(PDOStatement, fetchObject) ce = zend_standard_class_def; } - if (ctor_args && zend_hash_num_elements(ctor_args) && ce->constructor == NULL) { + if (UNEXPECTED(ce->constructor == NULL)) { + zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (ctor_args && zend_hash_num_elements(ctor_args) && zend_is_pass_function(ce->constructor)) { zend_argument_value_error(2, "must be empty when class provided in argument #1 ($class) does not have a constructor"); RETURN_THROWS(); } @@ -1190,9 +1196,13 @@ PHP_METHOD(PDOStatement, fetchAll) } else { fetch_class = zend_standard_class_def; } + if (UNEXPECTED(fetch_class->constructor == NULL)) { + zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(fetch_class->name)); + RETURN_THROWS(); + } if (ctor_args && zend_hash_num_elements(ctor_args) > 0) { - if (fetch_class->constructor == NULL) { + if (zend_is_pass_function(fetch_class->constructor)) { zend_argument_value_error(3, "must be empty when class provided in argument #2 ($class) does not have a constructor"); RETURN_THROWS(); } @@ -1706,6 +1716,10 @@ bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_a zend_argument_type_error(arg1_arg_num, "must be a valid class"); return false; } + if (UNEXPECTED(cep->constructor == NULL)) { + zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(cep->name)); + return false; + } /* Verify constructor_args (args[1]) is ?array */ /* TODO: Improve logic? */ if (variadic_num_args == 2) { @@ -1715,7 +1729,7 @@ bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_a return false; } if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) { - if (cep->constructor == NULL) { + if (zend_is_pass_function(cep->constructor)) { zend_argument_value_error(3, "must be empty when class provided in argument #2 ($class) does not have a constructor"); return false; } @@ -2375,12 +2389,6 @@ static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose return props; } -static zend_function *row_get_ctor(zend_object *object) -{ - zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually"); - return NULL; -} - static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) { ZEND_IGNORE_VALUE(object); @@ -2447,6 +2455,5 @@ void pdo_stmt_init(void) pdo_row_object_handlers.has_dimension = row_dim_exists; pdo_row_object_handlers.unset_dimension = row_dim_delete; pdo_row_object_handlers.get_properties_for = row_get_properties_for; - pdo_row_object_handlers.get_constructor = row_get_ctor; pdo_row_object_handlers.compare = zend_objects_not_comparable; } diff --git a/ext/pdo/pdo_stmt.stub.php b/ext/pdo/pdo_stmt.stub.php index b5783d72684a..ffde6c0d148f 100644 --- a/ext/pdo/pdo_stmt.stub.php +++ b/ext/pdo/pdo_stmt.stub.php @@ -68,6 +68,7 @@ public function getIterator(): Iterator {} } /** @not-serializable */ +#[\NonInstantiableClass("A PDORow class cannot be manually instantiated")] final class PDORow { public string $queryString; diff --git a/ext/pdo/pdo_stmt_arginfo.h b/ext/pdo/pdo_stmt_arginfo.h index 080202f897bc..789bfd69ab98 100644 --- a/ext/pdo/pdo_stmt_arginfo.h +++ b/ext/pdo/pdo_stmt_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit pdo_stmt.stub.php instead. - * Stub hash: 6a5b332ba4bfeceaca6aad734d38dabb66d82c97 */ + * Stub hash: f40ef0875271b733d862d5c0e3b23523428f2ff7 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDOStatement_bindColumn, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_MASK(0, column, MAY_BE_STRING|MAY_BE_LONG, NULL) @@ -162,5 +162,13 @@ static zend_class_entry *register_class_PDORow(void) zend_declare_typed_property(class_entry, property_queryString_name, &property_queryString_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_queryString_name, true); + zend_string *attribute_name_NonInstantiableClass_class_PDORow_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_PDORow_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_PDORow_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_PDORow_0, true); + zend_string *attribute_NonInstantiableClass_class_PDORow_0_arg0_str = zend_string_init("A PDORow class cannot be manually instantiated", strlen("A PDORow class cannot be manually instantiated"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_PDORow_0->args[0].value, attribute_NonInstantiableClass_class_PDORow_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/pdo/tests/pdo_036.phpt b/ext/pdo/tests/pdo_036.phpt index 10fe22193017..0ca74fa98a63 100644 --- a/ext/pdo/tests/pdo_036.phpt +++ b/ext/pdo/tests/pdo_036.phpt @@ -36,7 +36,7 @@ object(PDOStatement)#2 (1) { } Property queryString is read only -Fatal error: Uncaught PDOException: You may not create a PDORow manually in %spdo_036.php:%d +Fatal error: Uncaught ReflectionException: Class PDORow cannot be instantiated manually in %s:%d Stack trace: #0 %spdo_036.php(%d): ReflectionClass->newInstance() #1 {main} diff --git a/ext/pdo/tests/pdo_fetch_class_opaque_object.phpt b/ext/pdo/tests/pdo_fetch_class_opaque_object.phpt index eaef3f6e8f35..34fe6d1f5149 100644 --- a/ext/pdo/tests/pdo_fetch_class_opaque_object.phpt +++ b/ext/pdo/tests/pdo_fetch_class_opaque_object.phpt @@ -23,7 +23,11 @@ $db->exec("INSERT INTO pdo_fetch_class_opaque_object VALUES(3, 'CC')"); $stmt = $db->prepare('SELECT path FROM pdo_fetch_class_opaque_object'); $stmt->execute(); -var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Directory', [])); +try { + var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Directory', [])); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} ?> --CLEAN-- ---EXPECTF-- -array(3) { - [0]=> - object(Directory)#%s (1) { - ["path"]=> - string(2) "AA" - ["handle"]=> - uninitialized(mixed) - } - [1]=> - object(Directory)#%s (1) { - ["path"]=> - string(2) "BB" - ["handle"]=> - uninitialized(mixed) - } - [2]=> - object(Directory)#%s (1) { - ["path"]=> - string(2) "CC" - ["handle"]=> - uninitialized(mixed) - } -} +--EXPECT-- +Error: Cannot instantiate an object of class Directory diff --git a/ext/pdo/tests/pdorow.phpt b/ext/pdo/tests/pdorow.phpt index 92870e97a744..3752361163cd 100644 --- a/ext/pdo/tests/pdorow.phpt +++ b/ext/pdo/tests/pdorow.phpt @@ -9,7 +9,7 @@ new PDORow; ?> --EXPECTF-- -Fatal error: Uncaught PDOException: You may not create a PDORow manually in %spdorow.php:3 +Fatal error: Uncaught Error: A PDORow class cannot be manually instantiated in %s:%d Stack trace: #0 {main} thrown in %spdorow.php on line 3 diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 8efd43ed3a8b..8026dd82e67a 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -166,11 +166,6 @@ static zend_object *pgsql_link_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *pgsql_link_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead"); - return NULL; -} - static void pgsql_link_free(pgsql_link_handle *link) { PGresult *res; @@ -222,11 +217,6 @@ static zend_object *pgsql_result_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *pgsql_result_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct PgSql\\Result, use a dedicated function instead"); - return NULL; -} - static void pgsql_result_free(pgsql_result_handle *pg_result) { PQclear(pg_result->result); @@ -259,11 +249,6 @@ static zend_object *pgsql_lob_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *pgsql_lob_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct PgSql\\Lob, use pg_lo_open() instead"); - return NULL; -} - static void pgsql_lob_free_obj(zend_object *obj) { pgLofp *lofp = pgsql_lob_from_obj(obj); @@ -576,7 +561,6 @@ PHP_MINIT_FUNCTION(pgsql) memcpy(&pgsql_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pgsql_link_object_handlers.offset = offsetof(pgsql_link_handle, std); pgsql_link_object_handlers.free_obj = pgsql_link_free_obj; - pgsql_link_object_handlers.get_constructor = pgsql_link_get_constructor; pgsql_link_object_handlers.clone_obj = NULL; pgsql_link_object_handlers.compare = zend_objects_not_comparable; @@ -587,7 +571,6 @@ PHP_MINIT_FUNCTION(pgsql) memcpy(&pgsql_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pgsql_result_object_handlers.offset = offsetof(pgsql_result_handle, std); pgsql_result_object_handlers.free_obj = pgsql_result_free_obj; - pgsql_result_object_handlers.get_constructor = pgsql_result_get_constructor; pgsql_result_object_handlers.clone_obj = NULL; pgsql_result_object_handlers.compare = zend_objects_not_comparable; @@ -598,7 +581,6 @@ PHP_MINIT_FUNCTION(pgsql) memcpy(&pgsql_lob_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pgsql_lob_object_handlers.offset = offsetof(pgLofp, std); pgsql_lob_object_handlers.free_obj = pgsql_lob_free_obj; - pgsql_lob_object_handlers.get_constructor = pgsql_lob_get_constructor; pgsql_lob_object_handlers.clone_obj = NULL; pgsql_lob_object_handlers.compare = zend_objects_not_comparable; @@ -2129,6 +2111,13 @@ PHP_FUNCTION(pg_fetch_object) ce = zend_standard_class_def; } + /* TODO: Should only public constructors be allowed? */ + zend_function *constructor = zend_get_accessible_constructor_in_scope(ce, ce); + if (UNEXPECTED(ce->constructor == NULL)) { + zend_argument_value_error(3, "must be an instantiable class"); + RETURN_THROWS(); + } + if (UNEXPECTED(object_init_ex(return_value, ce) == FAILURE)) { RETURN_THROWS(); } @@ -2148,17 +2137,7 @@ PHP_FUNCTION(pg_fetch_object) } zend_object *obj = Z_OBJ_P(return_value); - const zend_class_entry *old = EG(fake_scope); - EG(fake_scope) = ce; - zend_function *constructor = obj->handlers->get_constructor(obj); - EG(fake_scope) = old; - - if (UNEXPECTED(EG(exception))) { - /* visibility error or override refused - VM dtors return_value */ - return; - } - - if (UNEXPECTED(!constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0)) { + if (UNEXPECTED(zend_is_pass_function(constructor) && ctor_params && zend_hash_num_elements(ctor_params) > 0)) { zend_argument_value_error(4, "must be empty when the specified class (%s) does not have a constructor", ZSTR_VAL(ce->name) @@ -2166,7 +2145,7 @@ PHP_FUNCTION(pg_fetch_object) RETURN_THROWS(); } - if (constructor) { + if (!zend_is_pass_function(constructor)) { zend_call_known_function(constructor, obj, ce, /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); if (EG(exception)) { diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index 52ddc3b3748a..df5b047f6258 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -965,6 +965,7 @@ function pg_close_stmt(Pgsql\Connection $connection, string $statement_name): Pg * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead")] final class Connection { } @@ -973,6 +974,7 @@ final class Connection * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct PgSql\\Result, use a dedicated function instead")] final class Result { } @@ -981,6 +983,7 @@ final class Result * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct PgSql\\Connection, use pg_lo_open() instead")] final class Lob { } diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 63a1d185d535..cc4df70e92ca 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit pgsql.stub.php instead. - * Stub hash: f25b5a574c96d4bc2f08b8cacab16f499a164a6b */ + * Stub hash: 93b2a5552d8409f2f31be6162b7be0836e771b28 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) @@ -1031,6 +1031,14 @@ static zend_class_entry *register_class_PgSql_Connection(void) INIT_NS_CLASS_ENTRY(ce, "PgSql", "Connection", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_PgSql_Connection_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_PgSql_Connection_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_PgSql_Connection_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_PgSql_Connection_0, true); + zend_string *attribute_NonInstantiableClass_class_PgSql_Connection_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead", strlen("Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Connection_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Connection_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1041,6 +1049,14 @@ static zend_class_entry *register_class_PgSql_Result(void) INIT_NS_CLASS_ENTRY(ce, "PgSql", "Result", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_PgSql_Result_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_PgSql_Result_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_PgSql_Result_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_PgSql_Result_0, true); + zend_string *attribute_NonInstantiableClass_class_PgSql_Result_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Result, use a dedicated function instead", strlen("Cannot directly construct PgSql\\Result, use a dedicated function instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Result_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Result_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1051,5 +1067,13 @@ static zend_class_entry *register_class_PgSql_Lob(void) INIT_NS_CLASS_ENTRY(ce, "PgSql", "Lob", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_PgSql_Lob_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_PgSql_Lob_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_PgSql_Lob_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_PgSql_Lob_0, true); + zend_string *attribute_NonInstantiableClass_class_PgSql_Lob_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Connection, use pg_lo_open() instead", strlen("Cannot directly construct PgSql\\Connection, use pg_lo_open() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Lob_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Lob_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt b/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt index b3e6ea7f7b36..afd295bf28d7 100644 --- a/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt +++ b/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt @@ -54,6 +54,6 @@ $db = @pg_connect($conn_str); @pg_query($db, "DROP TABLE IF EXISTS pg_fetch_object_abstract_class cascade"); ?> --EXPECT-- -Error: Cannot instantiate interface I +ValueError: pg_fetch_object(): Argument #3 ($class) must be an instantiable class Error: Cannot instantiate abstract class C -Error: Cannot instantiate enum E +ValueError: pg_fetch_object(): Argument #3 ($class) must be an instantiable class diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 50bcf4cb79c1..8442435d0986 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4451,7 +4451,7 @@ ZEND_METHOD(ReflectionClass, getConstructor) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - if (ce->constructor) { + if (ce->constructor && !zend_is_pass_function(ce->constructor)) { reflection_method_factory(ce, ce->constructor, NULL, return_value); } else { RETURN_NULL(); @@ -4517,6 +4517,10 @@ ZEND_METHOD(ReflectionClass, getMethod) /* {{{ _addmethod */ static bool _addmethod(zend_function *mptr, zend_class_entry *ce, HashTable *ht, zend_long filter) { + /* Skip fake constructor */ + if (zend_is_pass_function(mptr)) { + return false; + } if ((mptr->common.fn_flags & ZEND_ACC_PRIVATE) && mptr->common.scope != ce) { return false; } @@ -4905,10 +4909,9 @@ ZEND_METHOD(ReflectionClass, isInstantiable) RETURN_FALSE; } - /* Basically, the class is instantiable. Though, if there is a constructor - * and it is not publicly accessible, it isn't! */ - if (!ce->constructor) { - RETURN_TRUE; + /* Classes marked with the #[\NonInstantiableClass()] attribute are not instantiable */ + if (ce->constructor == NULL) { + RETURN_FALSE; } RETURN_BOOL(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC); @@ -5017,35 +5020,35 @@ ZEND_METHOD(ReflectionClass, newInstance) { reflection_object *intern; zend_class_entry *ce; - zend_function *constructor; GET_REFLECTION_OBJECT_PTR(ce); - if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) { - return; + zend_function *constructor = zend_get_public_constructor(ce); + if (UNEXPECTED(constructor == NULL)) { + if (ce->constructor == NULL) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); + } else { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); + } + RETURN_THROWS(); } - const zend_class_entry *old_scope = EG(fake_scope); - EG(fake_scope) = ce; - constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value)); - EG(fake_scope) = old_scope; - - /* Run the constructor if there is one */ - if (constructor) { - zval *params; - int num_args; - HashTable *named_params; + if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) { + RETURN_THROWS(); + } - if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); - zval_ptr_dtor(return_value); - RETURN_NULL(); - } + /* Run the constructor */ + zval *params; + uint32_t num_args; + HashTable *named_params; - ZEND_PARSE_PARAMETERS_START(0, -1) - Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params) + ZEND_PARSE_PARAMETERS_END(); + /* Run the constructor if there is one */ + if (!zend_is_pass_function(constructor)) { zend_call_known_function( constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, num_args, params, named_params); @@ -5053,8 +5056,6 @@ ZEND_METHOD(ReflectionClass, newInstance) if (EG(exception)) { zend_object_store_ctor_failed(Z_OBJ_P(return_value)); } - } else if (ZEND_NUM_ARGS()) { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ZSTR_VAL(ce->name)); } } /* }}} */ @@ -5069,6 +5070,12 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor) ZEND_PARSE_PARAMETERS_NONE(); + if (UNEXPECTED(ce->constructor == NULL)) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + if (ce->type == ZEND_INTERNAL_CLASS && ce->create_object != NULL && (ce->ce_flags & ZEND_ACC_FINAL)) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class %s is an internal class marked as final that cannot be instantiated without invoking its constructor", ZSTR_VAL(ce->name)); @@ -5084,9 +5091,7 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs) { reflection_object *intern; zend_class_entry *ce; - int argc = 0; HashTable *args = NULL; - zend_function *constructor; GET_REFLECTION_OBJECT_PTR(ce); @@ -5094,35 +5099,29 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs) RETURN_THROWS(); } - if (args) { - argc = zend_hash_num_elements(args); + zend_function *constructor = zend_get_public_constructor(ce); + if (UNEXPECTED(constructor == NULL)) { + if (ce->constructor == NULL) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); + } else { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); + } + RETURN_THROWS(); } if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) { return; } - const zend_class_entry *old_scope = EG(fake_scope); - EG(fake_scope) = ce; - constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value)); - EG(fake_scope) = old_scope; - /* Run the constructor if there is one */ - if (constructor) { - if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); - zval_ptr_dtor(return_value); - RETURN_NULL(); - } - + if (!zend_is_pass_function(constructor)) { zend_call_known_function( constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args); if (EG(exception)) { zend_object_store_ctor_failed(Z_OBJ_P(return_value)); } - } else if (argc) { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ZSTR_VAL(ce->name)); } } /* }}} */ diff --git a/ext/reflection/tests/007.phpt b/ext/reflection/tests/007.phpt index 8e90f7a010a9..172d46539195 100644 --- a/ext/reflection/tests/007.phpt +++ b/ext/reflection/tests/007.phpt @@ -97,9 +97,11 @@ string(43) "Class "Class_does_not_exist" does not exist" object(NoCtor)#%d (0) { } ====>newInstance(25) -string(86) "Class NoCtor does not have a constructor, so you cannot pass any constructor arguments" +object(NoCtor)#%d (0) { +} ====>newInstance(25, 42) -string(86) "Class NoCtor does not have a constructor, so you cannot pass any constructor arguments" +object(NoCtor)#%d (0) { +} ====>WithCtor ====>newInstance() diff --git a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt index 7dded54d3fae..622d9a35b446 100644 --- a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt +++ b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt @@ -59,7 +59,7 @@ var_dump($e1); try { $e2 = $rcE->newInstanceArgs(array('x')); - echo "you should not see this\n"; + echo "No explicit construct still allows instantiation\n"; } catch (Exception $e) { echo $e->getMessage() . "\n"; } @@ -73,4 +73,4 @@ Access to non-public constructor of class C Access to non-public constructor of class D object(E)#%d (0) { } -Class E does not have a constructor, so you cannot pass any constructor arguments +No explicit construct still allows instantiation diff --git a/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt b/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt index 59337f09e8b4..14e51c499082 100644 --- a/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt +++ b/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt @@ -42,7 +42,7 @@ object(stdClass)#%d (0) { } object(DateTime)#%d (0) { } -Class Generator is an internal class marked as final that cannot be instantiated without invoking its constructor +Class Generator cannot be instantiated manually object(Bar)#%d (1) { ["storage":"ArrayObject":private]=> array(0) { diff --git a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt index 22d06e328270..c67aceb3137d 100644 --- a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt +++ b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt @@ -62,7 +62,7 @@ var_dump($e1); try { $e2 = $rcE->newInstance('x'); - echo "you should not see this\n"; + echo "Can pass extra args to implicit constructor\n"; } catch (Exception $e) { echo $e->getMessage() . "\n"; } @@ -76,4 +76,4 @@ Access to non-public constructor of class C Access to non-public constructor of class D object(E)#%d (0) { } -Class E does not have a constructor, so you cannot pass any constructor arguments +Can pass extra args to implicit constructor diff --git a/ext/reflection/tests/bug52854.phpt b/ext/reflection/tests/bug52854.phpt index 2d174bc11eba..962a699e1124 100644 --- a/ext/reflection/tests/bug52854.phpt +++ b/ext/reflection/tests/bug52854.phpt @@ -25,4 +25,5 @@ object(Test)#%d (0) { } object(Test)#%d (0) { } -Class Test does not have a constructor, so you cannot pass any constructor arguments +object(Test)#%d (0) { +} diff --git a/ext/reflection/tests/bug64007.phpt b/ext/reflection/tests/bug64007.phpt index a25beb6360d7..eabb7a7c5ca4 100644 --- a/ext/reflection/tests/bug64007.phpt +++ b/ext/reflection/tests/bug64007.phpt @@ -10,14 +10,14 @@ try { var_dump($e->getMessage()); } -$generator = $reflection->newInstance(); +$generator = $reflection->newInstance(); var_dump($generator); ?> --EXPECTF-- -string(%d) "Class Generator is an internal class marked as final that cannot be instantiated without invoking its constructor" +string(47) "Class Generator cannot be instantiated manually" -Fatal error: Uncaught Error: The "Generator" class is reserved for internal use and cannot be manually instantiated in %sbug64007.php:%d +Fatal error: Uncaught ReflectionException: Class Generator cannot be instantiated manually in %s:%d Stack trace: #0 %s(%d): ReflectionClass->newInstance() #1 {main} - thrown in %sbug64007.php on line %d + thrown in %s on line %d diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c index 8721e4333ce8..99b23e1b05da 100644 --- a/ext/shmop/shmop.c +++ b/ext/shmop/shmop.c @@ -85,12 +85,6 @@ static zend_object *shmop_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *shmop_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Shmop, use shmop_open() instead"); - return NULL; -} - static void shmop_free_obj(zend_object *object) { php_shmop *shmop = shmop_from_obj(object); @@ -110,7 +104,6 @@ PHP_MINIT_FUNCTION(shmop) memcpy(&shmop_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); shmop_object_handlers.offset = offsetof(php_shmop, std); shmop_object_handlers.free_obj = shmop_free_obj; - shmop_object_handlers.get_constructor = shmop_get_constructor; shmop_object_handlers.clone_obj = NULL; shmop_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/shmop/shmop.stub.php b/ext/shmop/shmop.stub.php index 1694105745a8..96cb76ef5c37 100644 --- a/ext/shmop/shmop.stub.php +++ b/ext/shmop/shmop.stub.php @@ -6,6 +6,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct Shmop, use shmop_open() instead")] final class Shmop {} function shmop_open(int $key, string $mode, int $permissions, int $size): Shmop|false {} diff --git a/ext/shmop/shmop_arginfo.h b/ext/shmop/shmop_arginfo.h index 9d88fe63c32b..ba78fd1ebd52 100644 --- a/ext/shmop/shmop_arginfo.h +++ b/ext/shmop/shmop_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit shmop.stub.php instead. - * Stub hash: e7f250077b6721539caee96afe4ed392396018f9 */ + * Stub hash: 0a6a1bf8f5803c3b41ccffe587b5111515b83bf5 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_shmop_open, 0, 4, Shmop, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) @@ -67,5 +67,13 @@ static zend_class_entry *register_class_Shmop(void) INIT_CLASS_ENTRY(ce, "Shmop", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Shmop_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Shmop_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Shmop_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Shmop_0, true); + zend_string *attribute_NonInstantiableClass_class_Shmop_0_arg0_str = zend_string_init("Cannot directly construct Shmop, use shmop_open() instead", strlen("Cannot directly construct Shmop, use shmop_open() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Shmop_0->args[0].value, attribute_NonInstantiableClass_class_Shmop_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/soap/soap.c b/ext/soap/soap.c index da5e10ac3258..1fb3ac0ddb43 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -268,13 +268,6 @@ static void soap_url_object_free(zend_object *obj) zend_object_std_dtor(&url_obj->std); } -static zend_function *soap_url_object_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Soap\\Url"); - - return NULL; -} - static zend_result soap_url_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { @@ -315,13 +308,6 @@ static void soap_sdl_object_free(zend_object *obj) zend_object_std_dtor(&sdl_obj->std); } -static zend_function *soap_sdl_object_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Soap\\Sdl"); - - return NULL; -} - static zend_result soap_sdl_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { @@ -564,7 +550,6 @@ PHP_MINIT_FUNCTION(soap) memcpy(&soap_url_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); soap_url_object_handlers.offset = offsetof(soap_url_object, std); soap_url_object_handlers.free_obj = soap_url_object_free; - soap_url_object_handlers.get_constructor = soap_url_object_get_constructor; soap_url_object_handlers.clone_obj = NULL; soap_url_object_handlers.cast_object = soap_url_cast_object; soap_url_object_handlers.compare = zend_objects_not_comparable; @@ -576,7 +561,6 @@ PHP_MINIT_FUNCTION(soap) memcpy(&soap_sdl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); soap_sdl_object_handlers.offset = offsetof(soap_sdl_object, std); soap_sdl_object_handlers.free_obj = soap_sdl_object_free; - soap_sdl_object_handlers.get_constructor = soap_sdl_object_get_constructor; soap_sdl_object_handlers.clone_obj = NULL; soap_url_object_handlers.cast_object = soap_sdl_cast_object; soap_sdl_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/soap/soap.stub.php b/ext/soap/soap.stub.php index fdd4a46e109f..ad5ebf936c6f 100644 --- a/ext/soap/soap.stub.php +++ b/ext/soap/soap.stub.php @@ -7,6 +7,7 @@ * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct Soap\\Url")] final class Url { } @@ -15,6 +16,7 @@ final class Url * @strict-properties * @not-serializable */ + #[\NonInstantiableClass("Cannot directly construct Soap\\Sdl")] final class Sdl { } diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index 2f7d56ca4221..e5186f4b6eb4 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit soap.stub.php instead. - * Stub hash: 14c74a5d6f547837f536920d5abb741e2b6e4373 */ + * Stub hash: 8f2911fbdbcce57883cf744bb0f0be4879389418 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_use_soap_error_handler, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable, _IS_BOOL, 0, "true") @@ -333,6 +333,14 @@ static zend_class_entry *register_class_Soap_Url(void) INIT_NS_CLASS_ENTRY(ce, "Soap", "Url", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Soap_Url_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Soap_Url_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Soap_Url_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Soap_Url_0, true); + zend_string *attribute_NonInstantiableClass_class_Soap_Url_0_arg0_str = zend_string_init("Cannot directly construct Soap\\Url", strlen("Cannot directly construct Soap\\Url"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Soap_Url_0->args[0].value, attribute_NonInstantiableClass_class_Soap_Url_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -343,6 +351,14 @@ static zend_class_entry *register_class_Soap_Sdl(void) INIT_NS_CLASS_ENTRY(ce, "Soap", "Sdl", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Soap_Sdl_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Soap_Sdl_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Soap_Sdl_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Soap_Sdl_0, true); + zend_string *attribute_NonInstantiableClass_class_Soap_Sdl_0_arg0_str = zend_string_init("Cannot directly construct Soap\\Sdl", strlen("Cannot directly construct Soap\\Sdl"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Soap_Sdl_0->args[0].value, attribute_NonInstantiableClass_class_Soap_Sdl_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 7b87a2a2f9bd..d2cb66fa6edc 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -27,6 +27,7 @@ #include "ext/standard/file.h" #include "ext/standard/info.h" #include "php_ini.h" +#include "zend_attributes.h" #ifdef PHP_WIN32 # include "windows_common.h" # include @@ -140,11 +141,6 @@ static zend_object *socket_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *socket_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct Socket, use socket_create() instead"); - return NULL; -} - static void socket_free_obj(zend_object *object) { php_socket *socket = socket_from_obj(object); @@ -195,11 +191,6 @@ static zend_object *address_info_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *address_info_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead"); - return NULL; -} - static void address_info_free_obj(zend_object *object) { php_addrinfo *address_info = address_info_from_obj(object); @@ -486,7 +477,6 @@ static PHP_MINIT_FUNCTION(sockets) memcpy(&socket_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); socket_object_handlers.offset = offsetof(php_socket, std); socket_object_handlers.free_obj = socket_free_obj; - socket_object_handlers.get_constructor = socket_get_constructor; socket_object_handlers.clone_obj = NULL; socket_object_handlers.get_gc = socket_get_gc; socket_object_handlers.compare = zend_objects_not_comparable; @@ -498,7 +488,6 @@ static PHP_MINIT_FUNCTION(sockets) memcpy(&address_info_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); address_info_object_handlers.offset = offsetof(php_addrinfo, std); address_info_object_handlers.free_obj = address_info_free_obj; - address_info_object_handlers.get_constructor = address_info_get_constructor; address_info_object_handlers.clone_obj = NULL; address_info_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 56b2ac07e868..511b4226fe81 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -2201,6 +2201,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct Socket, use socket_create() instead")] final class Socket { } @@ -2209,6 +2210,7 @@ final class Socket * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead")] final class AddressInfo { } diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 2592cb740865..1c978647bea7 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sockets.stub.php instead. - * Stub hash: 5e71ef16f2121bd6c75794673d0e0a394759ff8b */ + * Stub hash: 38a80f6141e44f7d517df52f5e8e82e0accd3e33 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -1118,6 +1118,14 @@ static zend_class_entry *register_class_Socket(void) INIT_CLASS_ENTRY(ce, "Socket", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_Socket_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Socket_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Socket_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Socket_0, true); + zend_string *attribute_NonInstantiableClass_class_Socket_0_arg0_str = zend_string_init("Cannot directly construct Socket, use socket_create() instead", strlen("Cannot directly construct Socket, use socket_create() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Socket_0->args[0].value, attribute_NonInstantiableClass_class_Socket_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -1128,5 +1136,13 @@ static zend_class_entry *register_class_AddressInfo(void) INIT_CLASS_ENTRY(ce, "AddressInfo", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_AddressInfo_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_AddressInfo_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_AddressInfo_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_AddressInfo_0, true); + zend_string *attribute_NonInstantiableClass_class_AddressInfo_0_arg0_str = zend_string_init("Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead", strlen("Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_AddressInfo_0->args[0].value, attribute_NonInstantiableClass_class_AddressInfo_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 2a0e7a786738..bcf6fdfbb4f4 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -289,6 +289,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ register_basic_functions_symbols(module_number); + /* TODO: Should manual instantiation be allowed? */ php_ce_incomplete_class = register_class___PHP_Incomplete_Class(); php_register_incomplete_class_handlers(); diff --git a/ext/standard/dir.c b/ext/standard/dir.c index f313d5f539d1..f6f6f16caded 100644 --- a/ext/standard/dir.c +++ b/ext/standard/dir.c @@ -14,6 +14,7 @@ /* {{{ includes/startup/misc */ +#include "zend_attributes.h" #include "php.h" #include "fopen_wrappers.h" #include "file.h" @@ -51,12 +52,6 @@ static zend_object_handlers dir_class_object_handlers; #define Z_DIRECTORY_PATH_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 0) #define Z_DIRECTORY_HANDLE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 1) -static zend_function *dir_class_get_constructor(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct Directory, use dir() instead"); - return NULL; -} - static void php_set_default_dir(zend_resource *res) { if (DIRG(default_dir)) { @@ -90,7 +85,6 @@ PHP_MINIT_FUNCTION(dir) dir_class_entry_ptr->default_object_handlers = &dir_class_object_handlers; memcpy(&dir_class_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - dir_class_object_handlers.get_constructor = dir_class_get_constructor; dir_class_object_handlers.clone_obj = NULL; dir_class_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/standard/dir.stub.php b/ext/standard/dir.stub.php index 6b5ef51c13bb..c964dff4a02d 100644 --- a/ext/standard/dir.stub.php +++ b/ext/standard/dir.stub.php @@ -89,6 +89,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct Directory, use dir() instead")] final class Directory { public readonly string $path; diff --git a/ext/standard/dir_arginfo.h b/ext/standard/dir_arginfo.h index 7ff39528d526..1cf196fbb5e3 100644 --- a/ext/standard/dir_arginfo.h +++ b/ext/standard/dir_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit dir.stub.php instead. - * Stub hash: e21d382cd4001001874c49d8c5244efb57613910 */ + * Stub hash: 51ee840fe6f7af828d6f031730136b046f8ffc55 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Directory_close, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -70,5 +70,13 @@ static zend_class_entry *register_class_Directory(void) zend_declare_typed_property(class_entry, property_handle_name, &property_handle_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY)); zend_string_release_ex(property_handle_name, true); + zend_string *attribute_name_NonInstantiableClass_class_Directory_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_Directory_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_Directory_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_Directory_0, true); + zend_string *attribute_NonInstantiableClass_class_Directory_0_arg0_str = zend_string_init("Cannot directly construct Directory, use dir() instead", strlen("Cannot directly construct Directory, use dir() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_Directory_0->args[0].value, attribute_NonInstantiableClass_class_Directory_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt index 95999581f31c..a86868a92049 100644 --- a/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt +++ b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt @@ -11,20 +11,7 @@ try { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } -var_dump($d); -try { - var_dump($d->read()); -} catch (\Throwable $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; -} - ?> --EXPECT-- -bool(true) -object(Directory)#2 (0) { - ["path"]=> - uninitialized(string) - ["handle"]=> - uninitialized(mixed) -} -Error: Internal directory stream has been altered +bool(false) +ReflectionException: Class Directory cannot be instantiated manually diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index 16b4ffec50ee..814d73d92060 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -19,9 +19,10 @@ #include "php.h" #include "ext/standard/info.h" #include "php_sysvmsg.h" -#include "sysvmsg_arginfo.h" #include "ext/standard/php_var.h" #include "zend_smart_str.h" +#include "zend_attributes.h" +#include "sysvmsg_arginfo.h" #include #include @@ -80,11 +81,6 @@ static zend_object *sysvmsg_queue_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *sysvmsg_queue_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct SysvMessageQueue, use msg_get_queue() instead"); - return NULL; -} - static void sysvmsg_queue_free_obj(zend_object *object) { sysvmsg_queue_t *sysvmsg_queue = sysvmsg_queue_from_obj(object); @@ -103,7 +99,6 @@ PHP_MINIT_FUNCTION(sysvmsg) memcpy(&sysvmsg_queue_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); sysvmsg_queue_object_handlers.offset = offsetof(sysvmsg_queue_t, std); sysvmsg_queue_object_handlers.free_obj = sysvmsg_queue_free_obj; - sysvmsg_queue_object_handlers.get_constructor = sysvmsg_queue_get_constructor; sysvmsg_queue_object_handlers.clone_obj = NULL; sysvmsg_queue_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/sysvmsg/sysvmsg.stub.php b/ext/sysvmsg/sysvmsg.stub.php index 6f94167ac7b9..edaac05d3cce 100644 --- a/ext/sysvmsg/sysvmsg.stub.php +++ b/ext/sysvmsg/sysvmsg.stub.php @@ -32,6 +32,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct SysvMessageQueue, use msg_get_queue() instead")] final class SysvMessageQueue { } diff --git a/ext/sysvmsg/sysvmsg_arginfo.h b/ext/sysvmsg/sysvmsg_arginfo.h index 9dee3e0f4840..95a02c1360c0 100644 --- a/ext/sysvmsg/sysvmsg_arginfo.h +++ b/ext/sysvmsg/sysvmsg_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sysvmsg.stub.php instead. - * Stub hash: ed5b1e4e5dda6a65ce336fc4daa975520c354f17 */ + * Stub hash: 43f34e025ca7978faedc724af45952fe3fd16d4f */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_msg_get_queue, 0, 1, SysvMessageQueue, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) @@ -78,5 +78,13 @@ static zend_class_entry *register_class_SysvMessageQueue(void) INIT_CLASS_ENTRY(ce, "SysvMessageQueue", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_SysvMessageQueue_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_SysvMessageQueue_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_SysvMessageQueue_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_SysvMessageQueue_0, true); + zend_string *attribute_NonInstantiableClass_class_SysvMessageQueue_0_arg0_str = zend_string_init("Cannot directly construct SysvMessageQueue, use msg_get_queue() instead", strlen("Cannot directly construct SysvMessageQueue, use msg_get_queue() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_SysvMessageQueue_0->args[0].value, attribute_NonInstantiableClass_class_SysvMessageQueue_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index 9d3990d0bdc0..968daeb96334 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -26,9 +26,10 @@ #include #include -#include "sysvsem_arginfo.h" #include "php_sysvsem.h" #include "ext/standard/info.h" +#include "zend_attributes.h" +#include "sysvsem_arginfo.h" #ifndef HAVE_UNION_SEMUN union semun { @@ -97,11 +98,6 @@ static zend_object *sysvsem_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *sysvsem_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct SysvSemaphore, use sem_get() instead"); - return NULL; -} - static void sysvsem_free_obj(zend_object *object) { sysvsem_sem *sem_ptr = sysvsem_from_obj(object); @@ -149,7 +145,6 @@ PHP_MINIT_FUNCTION(sysvsem) memcpy(&sysvsem_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); sysvsem_object_handlers.offset = offsetof(sysvsem_sem, std); sysvsem_object_handlers.free_obj = sysvsem_free_obj; - sysvsem_object_handlers.get_constructor = sysvsem_get_constructor; sysvsem_object_handlers.clone_obj = NULL; sysvsem_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/sysvsem/sysvsem.stub.php b/ext/sysvsem/sysvsem.stub.php index d8e10a5f917e..4077e547a5d9 100644 --- a/ext/sysvsem/sysvsem.stub.php +++ b/ext/sysvsem/sysvsem.stub.php @@ -6,6 +6,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct SysvSemaphore, use sem_get() instead")] final class SysvSemaphore { } diff --git a/ext/sysvsem/sysvsem_arginfo.h b/ext/sysvsem/sysvsem_arginfo.h index b7643a926b14..82a5fcf9c18d 100644 --- a/ext/sysvsem/sysvsem_arginfo.h +++ b/ext/sysvsem/sysvsem_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sysvsem.stub.php instead. - * Stub hash: 946ea9d0d2156ced1bac460d7d5fc3420e1934bb */ + * Stub hash: 68b3058bcd5654ca98163bc42081cd31e792ff8d */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_sem_get, 0, 1, SysvSemaphore, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) @@ -39,5 +39,13 @@ static zend_class_entry *register_class_SysvSemaphore(void) INIT_CLASS_ENTRY(ce, "SysvSemaphore", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_SysvSemaphore_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_SysvSemaphore_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_SysvSemaphore_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_SysvSemaphore_0, true); + zend_string *attribute_NonInstantiableClass_class_SysvSemaphore_0_arg0_str = zend_string_init("Cannot directly construct SysvSemaphore, use sem_get() instead", strlen("Cannot directly construct SysvSemaphore, use sem_get() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_SysvSemaphore_0->args[0].value, attribute_NonInstantiableClass_class_SysvSemaphore_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c index 96324e67c44d..1f018a4d4f70 100644 --- a/ext/sysvshm/sysvshm.c +++ b/ext/sysvshm/sysvshm.c @@ -23,10 +23,11 @@ #include #include "php_sysvshm.h" -#include "sysvshm_arginfo.h" #include "ext/standard/info.h" #include "ext/standard/php_var.h" #include "zend_smart_str.h" +#include "zend_attributes.h" +#include "sysvshm_arginfo.h" /* SysvSharedMemory class */ @@ -48,11 +49,6 @@ static zend_object *sysvshm_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *sysvshm_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct SysvSharedMemory, use shm_attach() instead"); - return NULL; -} - static void sysvshm_free_obj(zend_object *object) { sysvshm_shm *sysvshm = sysvshm_from_obj(object); @@ -102,7 +98,6 @@ PHP_MINIT_FUNCTION(sysvshm) memcpy(&sysvshm_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); sysvshm_object_handlers.offset = offsetof(sysvshm_shm, std); sysvshm_object_handlers.free_obj = sysvshm_free_obj; - sysvshm_object_handlers.get_constructor = sysvshm_get_constructor; sysvshm_object_handlers.clone_obj = NULL; sysvshm_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/sysvshm/sysvshm.stub.php b/ext/sysvshm/sysvshm.stub.php index c89f67bc068a..1a9e74166e3a 100644 --- a/ext/sysvshm/sysvshm.stub.php +++ b/ext/sysvshm/sysvshm.stub.php @@ -6,6 +6,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct SysvSharedMemory, use shm_attach() instead")] final class SysvSharedMemory { } diff --git a/ext/sysvshm/sysvshm_arginfo.h b/ext/sysvshm/sysvshm_arginfo.h index aade5b8276a4..717769adc10d 100644 --- a/ext/sysvshm/sysvshm_arginfo.h +++ b/ext/sysvshm/sysvshm_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sysvshm.stub.php instead. - * Stub hash: 792c695a705678a3779d62cef8a5136069f98dee */ + * Stub hash: d5f2cf0533fa36b27345e610061cc4abf456da65 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_shm_attach, 0, 1, SysvSharedMemory, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) @@ -59,5 +59,13 @@ static zend_class_entry *register_class_SysvSharedMemory(void) INIT_CLASS_ENTRY(ce, "SysvSharedMemory", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_SysvSharedMemory_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_SysvSharedMemory_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_SysvSharedMemory_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_SysvSharedMemory_0, true); + zend_string *attribute_NonInstantiableClass_class_SysvSharedMemory_0_arg0_str = zend_string_init("Cannot directly construct SysvSharedMemory, use shm_attach() instead", strlen("Cannot directly construct SysvSharedMemory, use shm_attach() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_SysvSharedMemory_0->args[0].value, attribute_NonInstantiableClass_class_SysvSharedMemory_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 5bc502f2ef76..1fe8bea4bb3b 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -121,7 +121,6 @@ static PHP_GINIT_FUNCTION(xml); static zend_object *xml_parser_create_object(zend_class_entry *class_type); static void xml_parser_free_obj(zend_object *object); static HashTable *xml_parser_get_gc(zend_object *object, zval **table, int *n); -static zend_function *xml_parser_get_constructor(zend_object *object); static zend_string *xml_utf8_decode(const XML_Char *, size_t, const XML_Char *); inline static unsigned short xml_encode_iso_8859_1(unsigned char); @@ -225,7 +224,6 @@ PHP_MINIT_FUNCTION(xml) xml_parser_object_handlers.offset = offsetof(xml_parser, std); xml_parser_object_handlers.free_obj = xml_parser_free_obj; xml_parser_object_handlers.get_gc = xml_parser_get_gc; - xml_parser_object_handlers.get_constructor = xml_parser_get_constructor; xml_parser_object_handlers.clone_obj = NULL; xml_parser_object_handlers.compare = zend_objects_not_comparable; @@ -429,11 +427,6 @@ static HashTable *xml_parser_get_gc(zend_object *object, zval **table, int *n) return zend_std_get_properties(object); } -static zend_function *xml_parser_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead"); - return NULL; -} - /* This is always called to simplify the mess to deal with BC breaks, but only set a new handler if it is initialized */ static void xml_set_handler(zend_fcall_info_cache *const parser_handler, const zend_fcall_info_cache *const fn) { diff --git a/ext/xml/xml.stub.php b/ext/xml/xml.stub.php index f589c3b2c8f1..343be4929572 100644 --- a/ext/xml/xml.stub.php +++ b/ext/xml/xml.stub.php @@ -202,6 +202,7 @@ function xml_parser_get_option(XMLParser $parser, int $option): string|int|bool * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead")] final class XMLParser { } diff --git a/ext/xml/xml_arginfo.h b/ext/xml/xml_arginfo.h index 96430aef12bd..0adcff9466ce 100644 --- a/ext/xml/xml_arginfo.h +++ b/ext/xml/xml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit xml.stub.php instead. - * Stub hash: c7838fb209d601be280dfdebfd135906afa36e8c */ + * Stub hash: 9e143434b552f9360bbfef607bff65fd3511add0 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_xml_parser_create, 0, 0, XMLParser, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null") @@ -185,5 +185,13 @@ static zend_class_entry *register_class_XMLParser(void) INIT_CLASS_ENTRY(ce, "XMLParser", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_XMLParser_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_XMLParser_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_XMLParser_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_XMLParser_0, true); + zend_string *attribute_NonInstantiableClass_class_XMLParser_0_arg0_str = zend_string_init("Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead", strlen("Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_XMLParser_0->args[0].value, attribute_NonInstantiableClass_class_XMLParser_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/ext/zend_test/tests/zend_object_init_with_constructor.phpt b/ext/zend_test/tests/zend_object_init_with_constructor.phpt index 65b111447f0b..9a138e5ea531 100644 --- a/ext/zend_test/tests/zend_object_init_with_constructor.phpt +++ b/ext/zend_test/tests/zend_object_init_with_constructor.phpt @@ -58,6 +58,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("_ZendTestTrait"); @@ -65,6 +66,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("ZendTestUnitEnum"); @@ -72,6 +74,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("AbstractClass"); @@ -79,6 +82,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("SysvMessageQueue"); @@ -86,6 +90,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("PrivateUser"); @@ -93,6 +98,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("ThrowingUser"); @@ -100,6 +106,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } echo "Testing param passing\n"; @@ -109,6 +116,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", "str", 5); @@ -116,6 +124,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } try { $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", 5, string_param: "str", unused_param: 15.3); @@ -123,6 +132,7 @@ try { unset($o); } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; + unset($e); } $o = zend_object_init_with_constructor("TestUserWithConstructorArgs", 5, string_param: "str"); diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 3b987fa45e48..347e19b1eeb1 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -20,6 +20,7 @@ #include #endif +#include "zend_attributes.h" #include "php.h" #include "SAPI.h" #include "php_ini.h" @@ -62,11 +63,6 @@ static zend_object *inflate_context_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *inflate_context_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct InflateContext, use inflate_init() instead"); - return NULL; -} - static void inflate_context_free_obj(zend_object *object) { php_zlib_context *intern = inflate_context_from_obj(object); @@ -100,11 +96,6 @@ static zend_object *deflate_context_create_object(zend_class_entry *class_type) return &intern->std; } -static zend_function *deflate_context_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct DeflateContext, use deflate_init() instead"); - return NULL; -} - static void deflate_context_free_obj(zend_object *object) { php_zlib_context *intern = deflate_context_from_obj(object); @@ -1357,7 +1348,6 @@ static PHP_MINIT_FUNCTION(zlib) memcpy(&inflate_context_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); inflate_context_object_handlers.offset = offsetof(php_zlib_context, std); inflate_context_object_handlers.free_obj = inflate_context_free_obj; - inflate_context_object_handlers.get_constructor = inflate_context_get_constructor; inflate_context_object_handlers.clone_obj = NULL; inflate_context_object_handlers.compare = zend_objects_not_comparable; @@ -1368,7 +1358,6 @@ static PHP_MINIT_FUNCTION(zlib) memcpy(&deflate_context_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); deflate_context_object_handlers.offset = offsetof(php_zlib_context, std); deflate_context_object_handlers.free_obj = deflate_context_free_obj; - deflate_context_object_handlers.get_constructor = deflate_context_get_constructor; deflate_context_object_handlers.clone_obj = NULL; deflate_context_object_handlers.compare = zend_objects_not_comparable; diff --git a/ext/zlib/zlib.stub.php b/ext/zlib/zlib.stub.php index 06f0f6d3ae8e..f6e6e231a97d 100644 --- a/ext/zlib/zlib.stub.php +++ b/ext/zlib/zlib.stub.php @@ -147,6 +147,7 @@ * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct InflateContext, use inflate_init() instead")] final class InflateContext { } @@ -155,6 +156,7 @@ final class InflateContext * @strict-properties * @not-serializable */ +#[\NonInstantiableClass("Cannot directly construct DeflateContext, use deflate_init() instead")] final class DeflateContext { } diff --git a/ext/zlib/zlib_arginfo.h b/ext/zlib/zlib_arginfo.h index 22605924b8b1..c479c7e1e0b6 100644 --- a/ext/zlib/zlib_arginfo.h +++ b/ext/zlib/zlib_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zlib.stub.php instead. - * Stub hash: 4c5bea6d9f290c244c7bb27c77fe8007d43a40db */ + * Stub hash: 58b0352e30109a3207350f0bc8932304794cb169 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_gzhandler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) @@ -234,6 +234,14 @@ static zend_class_entry *register_class_InflateContext(void) INIT_CLASS_ENTRY(ce, "InflateContext", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_InflateContext_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_InflateContext_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_InflateContext_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_InflateContext_0, true); + zend_string *attribute_NonInstantiableClass_class_InflateContext_0_arg0_str = zend_string_init("Cannot directly construct InflateContext, use inflate_init() instead", strlen("Cannot directly construct InflateContext, use inflate_init() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_InflateContext_0->args[0].value, attribute_NonInstantiableClass_class_InflateContext_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } @@ -244,5 +252,13 @@ static zend_class_entry *register_class_DeflateContext(void) INIT_CLASS_ENTRY(ce, "DeflateContext", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_string *attribute_name_NonInstantiableClass_class_DeflateContext_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_DeflateContext_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_DeflateContext_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_DeflateContext_0, true); + zend_string *attribute_NonInstantiableClass_class_DeflateContext_0_arg0_str = zend_string_init("Cannot directly construct DeflateContext, use deflate_init() instead", strlen("Cannot directly construct DeflateContext, use deflate_init() instead"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_DeflateContext_0->args[0].value, attribute_NonInstantiableClass_class_DeflateContext_0_arg0_str); + + class_entry->constructor = NULL; + return class_entry; } diff --git a/sapi/fuzzer/fuzzer-sapi.c b/sapi/fuzzer/fuzzer-sapi.c index 96fe75ab7a8a..c5bbf721ce45 100644 --- a/sapi/fuzzer/fuzzer-sapi.c +++ b/sapi/fuzzer/fuzzer-sapi.c @@ -124,12 +124,6 @@ static sapi_module_struct fuzzer_module = { STANDARD_SAPI_MODULE_PROPERTIES }; -static ZEND_COLD zend_function *disable_class_get_constructor_handler(zend_object *obj) /* {{{ */ -{ - zend_throw_error(NULL, "Cannot construct class %s, as it is disabled", ZSTR_VAL(obj->ce->name)); - return NULL; -} - static void fuzzer_disable_classes(void) { /* Overwrite built-in constructor for InfiniteIterator as it @@ -137,10 +131,7 @@ static void fuzzer_disable_classes(void) /* Lowercase as this is how the CE as stored */ zend_class_entry *InfiniteIterator_class = zend_hash_str_find_ptr(CG(class_table), "infiniteiterator", strlen("infiniteiterator")); - static zend_object_handlers handlers; - memcpy(&handlers, InfiniteIterator_class->default_object_handlers, sizeof(handlers)); - handlers.get_constructor = disable_class_get_constructor_handler; - InfiniteIterator_class->default_object_handlers = &handlers; + zend_class_entry *InfiniteIterator_class->constructor = NULL; } int fuzzer_init_php(const char *extra_ini) From ddd472b52d76dc1e13c8c31a0f58aebc6c720073 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 May 2026 15:09:49 +0200 Subject: [PATCH 2/3] Use a private constructor instead Add some APIs so that we can stop relying on the get_constructor object handler --- Zend/Optimizer/escape_analysis.c | 4 +- Zend/Optimizer/zend_inference.c | 2 +- Zend/tests/closures/closure_instantiate.phpt | 8 +- .../tests/enum/no-new-through-reflection.phpt | 2 +- Zend/tests/traits/bug60173.phpt | 2 +- Zend/zend_API.c | 41 +---- Zend/zend_ast.c | 14 +- Zend/zend_closures_arginfo.h | 2 +- Zend/zend_compile.c | 39 +++- Zend/zend_compile.h | 8 +- Zend/zend_execute.c | 2 +- Zend/zend_execute.h | 3 + Zend/zend_generators_arginfo.h | 2 +- Zend/zend_inheritance.c | 8 +- Zend/zend_object_handlers.c | 33 +--- Zend/zend_objects_API.c | 69 ++++++- Zend/zend_objects_API.h | 39 ++-- Zend/zend_vm_def.h | 23 +-- Zend/zend_vm_execute.h | 174 ++++++++---------- build/gen_stub.php | 2 +- ext/com_dotnet/com_extension.c | 1 + ext/com_dotnet/com_extension.stub.php | 1 + ext/com_dotnet/com_extension_arginfo.h | 10 +- ext/com_dotnet/com_saproxy.c | 7 - ext/curl/curl_arginfo.h | 8 +- ext/dba/dba_arginfo.h | 2 +- ext/ffi/ffi_arginfo.h | 6 +- ext/ftp/ftp_arginfo.h | 2 +- ext/gd/gd_arginfo.h | 4 +- ext/ldap/ldap_arginfo.h | 6 +- ext/mysqli/mysqli.c | 9 +- ext/mysqli/tests/bug74968.phpt | 2 +- ext/mysqli/tests/mysqli_fetch_object.phpt | 16 +- ext/odbc/odbc_arginfo.h | 4 +- ext/opcache/zend_file_cache.c | 6 +- ext/openssl/openssl_arginfo.h | 8 +- ext/pdo/pdo_dbh.c | 15 +- ext/pdo/pdo_stmt.c | 14 +- ext/pdo/pdo_stmt_arginfo.h | 2 +- ...R_STATEMENT_CLASS_with_abstract_class.phpt | 1 - ext/pdo/tests/pdo_036.phpt | 2 +- ext/pgsql/pgsql.c | 26 ++- ext/pgsql/pgsql_arginfo.h | 6 +- .../pg_fetch_object_with_abstract_class.phpt | 6 +- ext/reflection/php_reflection.c | 60 +++--- ...onClass_newInstanceWithoutConstructor.phpt | 2 +- ext/reflection/tests/bug64007.phpt | 23 ++- ext/shmop/shmop_arginfo.h | 2 +- ext/soap/soap_arginfo.h | 4 +- ext/sockets/sockets_arginfo.h | 4 +- ext/standard/dir_arginfo.h | 2 +- ...flection_create_instance_no_construct.phpt | 2 +- ext/sysvmsg/sysvmsg_arginfo.h | 2 +- ext/sysvsem/sysvsem_arginfo.h | 2 +- ext/sysvshm/sysvshm_arginfo.h | 2 +- ext/xml/xml_arginfo.h | 2 +- ext/zlib/zlib_arginfo.h | 4 +- sapi/fuzzer/fuzzer-sapi.c | 2 +- 58 files changed, 374 insertions(+), 380 deletions(-) diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index ccdcb0dee6f8..8dbd6855d68d 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -167,7 +167,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object - && (!ce->constructor || zend_is_pass_function(ce->constructor)) + && !ce->constructor && !ce->destructor && !ce->__get && !ce->__set @@ -236,7 +236,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object - && (!ce->constructor || zend_is_pass_function(ce->constructor)) + && !ce->constructor && !ce->destructor && !ce->__get && !ce->__set diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index aba5b0eb8818..05d33d3d75fb 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3393,7 +3393,7 @@ static zend_always_inline zend_result _zend_update_type_info( } /* New objects without constructors cannot escape. */ if (ce - && (!ce->constructor || zend_is_pass_function(ce->constructor)) + && !ce->constructor && !ce->create_object && ce->default_object_handlers->get_constructor == zend_std_get_constructor) { tmp &= ~MAY_BE_RCN; diff --git a/Zend/tests/closures/closure_instantiate.phpt b/Zend/tests/closures/closure_instantiate.phpt index de3b866cb82e..03d2c45a475d 100644 --- a/Zend/tests/closures/closure_instantiate.phpt +++ b/Zend/tests/closures/closure_instantiate.phpt @@ -8,14 +8,10 @@ Mark Baker mark@lange.demon.co.uk at the PHPNW2017 Conference for PHP Testfest 2 try { // Closures should be instantiatable using new $x = new Closure(); -} catch (Exception $e) { - // Instantiating a closure is an error, not an exception, so we shouldn't see this - echo 'EXCEPTION: ', $e->getMessage(); } catch (Throwable $e) { - // This is the message that we should see for a caught error - echo 'ERROR: ', $e->getMessage(); + echo $e::class, ': ', $e->getMessage(); } ?> --EXPECT-- -ERROR: Instantiation of class Closure is not allowed +Error: Instantiation of class Closure is not allowed diff --git a/Zend/tests/enum/no-new-through-reflection.phpt b/Zend/tests/enum/no-new-through-reflection.phpt index 5ba61dc992d7..fa1e1e23c1bb 100644 --- a/Zend/tests/enum/no-new-through-reflection.phpt +++ b/Zend/tests/enum/no-new-through-reflection.phpt @@ -13,4 +13,4 @@ try { ?> --EXPECT-- -ReflectionException: Class Foo cannot be instantiated manually +ReflectionException: Cannot instantiate enum Foo diff --git a/Zend/tests/traits/bug60173.phpt b/Zend/tests/traits/bug60173.phpt index 664484cefe3e..120de3fb40d4 100644 --- a/Zend/tests/traits/bug60173.phpt +++ b/Zend/tests/traits/bug60173.phpt @@ -9,7 +9,7 @@ $rc = new ReflectionClass('foo'); $rc->newInstance(); ?> --EXPECTF-- -Fatal error: Uncaught ReflectionException: Class foo cannot be instantiated manually in %s:%d +Fatal error: Uncaught ReflectionException: Cannot instantiate trait foo in %s:%d Stack trace: #0 %s(%d): ReflectionClass->newInstance() #1 {main} diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e16d02ade43d..111ca2920327 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1793,16 +1793,7 @@ ZEND_API void object_properties_load(zend_object *object, const HashTable *prope static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */ { if (UNEXPECTED(class_type->ce_flags & ZEND_ACC_UNINSTANTIABLE)) { - if (class_type->ce_flags & ZEND_ACC_INTERFACE) { - zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_TRAIT) { - zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_ENUM) { - zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(class_type->name)); - } else { - ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)); - zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name)); - } + zend_cannot_instantiate_class(class_type, NULL); ZVAL_NULL(arg); Z_OBJ_P(arg) = NULL; return FAILURE; @@ -1846,25 +1837,7 @@ 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_function *constructor = class_type->constructor; - if (UNEXPECTED(constructor == NULL)) { - const zend_attribute *non_instantiable_class = zend_get_attribute_str(class_type->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - ZVAL_UNDEF(arg); - return FAILURE; - } - - if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) { - /* Use zend_bad_constructor_call() somehow? */ - zend_throw_error( - NULL, - "Call to %s %s::%s() from global scope", - zend_visibility_string(class_type->constructor->common.fn_flags), - ZSTR_VAL(class_type->constructor->common.scope->name), - ZSTR_VAL(class_type->constructor->common.function_name) - ); + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(class_type, zend_get_executed_scope()))) { ZVAL_UNDEF(arg); return FAILURE; } @@ -1876,8 +1849,9 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c } zend_object *obj = Z_OBJ_P(arg); - /* Fake constructor means we don't need to call the constructor actually */ - if (zend_is_pass_function(constructor)) { + zend_function *constructor = class_type->constructor; + /* No constructor, so no need to call it */ + if (constructor == NULL) { /* 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() */ @@ -3523,11 +3497,6 @@ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig if (class_entry->info.internal.builtin_functions) { zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); } - - /* Assign the pass function as a default constructor */ - if (class_entry->constructor == NULL) { - class_entry->constructor = (zend_function *) &zend_pass_function; - } lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); lowercase_name = zend_new_interned_string(lowercase_name); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a7e26711cd17..92edea6fdd2f 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1058,6 +1058,10 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( return FAILURE; } + if (!zend_is_class_instantiable(ce)) { + return FAILURE; + } + if (object_init_ex(result, ce) != SUCCESS) { return FAILURE; } @@ -1096,10 +1100,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } } - zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (ctor) { + if (ce->constructor) { zend_call_known_function( - ctor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args); + ce->constructor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args); } zend_array_destroy(args); @@ -1117,10 +1120,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } } - zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (ctor) { + if (ce->constructor) { zend_call_known_instance_method( - ctor, Z_OBJ_P(result), NULL, args_ast->children, args); + ce->constructor, Z_OBJ_P(result), NULL, args_ast->children, args); } for (uint32_t i = 0; i < args_ast->children; i++) { diff --git a/Zend/zend_closures_arginfo.h b/Zend/zend_closures_arginfo.h index 004430f6fb1b..9591cee8eb88 100644 --- a/Zend/zend_closures_arginfo.h +++ b/Zend/zend_closures_arginfo.h @@ -57,7 +57,7 @@ static zend_class_entry *register_class_Closure(void) zend_string *attribute_NonInstantiableClass_class_Closure_0_arg0_str = zend_string_init("Instantiation of class Closure is not allowed", strlen("Instantiation of class Closure is not allowed"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Closure_0->args[0].value, attribute_NonInstantiableClass_class_Closure_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bc38c0b8932b..779ef778d721 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5704,7 +5704,9 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ const zend_function *fbc = NULL; - if (ce && ce->constructor && is_func_accessible(ce->constructor)) { + if (ce + && ce->constructor + && is_func_accessible(ce->constructor)) { fbc = ce->constructor; } @@ -9527,6 +9529,33 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +static ZEND_FUNCTION(non_instantiable_constructor) +{ +} + +static zend_arg_info zend_non_instantiable_constructor_arg_info[1] = {0}; +ZEND_API const zend_internal_function zend_non_instantiable_constructor = { + ZEND_INTERNAL_FUNCTION, /* type */ + {0, 0, 0}, /* arg_flags */ + ZEND_ACC_PRIVATE, /* fn_flags */ + NULL, /* name */ + NULL, /* scope */ + NULL, /* prototype */ + 0, /* num_args */ + 0, /* required_num_args */ + zend_non_instantiable_constructor_arg_info + 1, /* arg_info */ + NULL, /* attributes */ + NULL, /* run_time_cache */ + NULL, /* doc_comment */ + 0, /* T */ + 0, /* fn_flags2 */ + NULL, /* prop_info */ + ZEND_FN(non_instantiable_constructor), /* handler */ + NULL, /* module */ + NULL, /* frameless_function_infos */ + {NULL,NULL,NULL,NULL} /* reserved */ +}; + static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool toplevel) /* {{{ */ { const zend_ast_decl *decl = (const zend_ast_decl *) ast; @@ -9648,11 +9677,11 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top ce->ce_flags |= ZEND_ACC_TOP_LEVEL; } - /* Add a default "pass" constructor if none are defined */ + /* Add zend_non_instantiable_constructor constructor if class cannot be manually instantiated */ if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - if (!non_instantiable_class) { - ce->constructor = (zend_function *) &zend_pass_function; + const zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + if (non_instantiable_class) { + ce->constructor = (zend_function *) &zend_non_instantiable_constructor; } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d88da25ea288..c1717af0d3d7 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -654,11 +654,11 @@ struct _zend_execute_data { zend_array *extra_named_params; }; -/* export zend_pass_function to allow comparisons against it */ -extern ZEND_API const zend_internal_function zend_pass_function; +/* export zend_non_instantiable_constructor to allow comparisons against it */ +extern ZEND_API const zend_internal_function zend_non_instantiable_constructor; -static zend_always_inline bool zend_is_pass_function(const zend_function *fn) { - return fn == (const zend_function*) &zend_pass_function; +static zend_always_inline bool zend_is_non_instantiable_constructor(const zend_function *fn) { + return fn == (const zend_function*) &zend_non_instantiable_constructor; } #define ZEND_CALL_HAS_THIS IS_OBJECT_EX diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 24567937bbc7..4253037fda52 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -140,7 +140,7 @@ static zend_arg_info zend_pass_function_arg_info[1] = {0}; ZEND_API const zend_internal_function zend_pass_function = { ZEND_INTERNAL_FUNCTION, /* type */ {0, 0, 0}, /* arg_flags */ - ZEND_ACC_PUBLIC, /* fn_flags */ + 0, /* fn_flags */ NULL, /* name */ NULL, /* scope */ NULL, /* prototype */ diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 7a63b6d90c02..ba48b19bcfe1 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -57,6 +57,9 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, const char *string_name, bool handle_exceptions); ZEND_API zend_result zend_eval_stringl_ex(const char *str, size_t str_len, zval *retval_ptr, const char *string_name, bool handle_exceptions); +/* export zend_pass_function to allow comparisons against it */ +extern ZEND_API const zend_internal_function zend_pass_function; + ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc); diff --git a/Zend/zend_generators_arginfo.h b/Zend/zend_generators_arginfo.h index f4a60830054d..7aa4fd28d4e8 100644 --- a/Zend/zend_generators_arginfo.h +++ b/Zend/zend_generators_arginfo.h @@ -64,7 +64,7 @@ static zend_class_entry *register_class_Generator(zend_class_entry *class_entry_ zend_string *attribute_NonInstantiableClass_class_Generator_0_arg0_str = zend_string_init("The \"Generator\" class is reserved for internal use and cannot be manually instantiated", strlen("The \"Generator\" class is reserved for internal use and cannot be manually instantiated"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Generator_0->args[0].value, attribute_NonInstantiableClass_class_Generator_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index efbc9f857163..e96955ee3be6 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -186,7 +186,7 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ ce->__debugInfo = parent->__debugInfo; } - if (ce->constructor && !zend_is_pass_function(ce->constructor)) { + if (ce->constructor) { if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { zend_error_noreturn(E_ERROR, "Cannot override final %s::__construct() with %s::__construct()", ZSTR_VAL(parent->name), @@ -195,7 +195,11 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ return; } - ce->constructor = parent->constructor; + if (zend_is_non_instantiable_constructor(parent->constructor)) { + ce->constructor = NULL; + } else { + ce->constructor = parent->constructor; + } } /* }}} */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index e7ddd466a51a..65797a1af5f6 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2173,40 +2173,9 @@ ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *c } /* }}} */ -static ZEND_COLD zend_never_inline void zend_bad_constructor_call(const zend_function *constructor, const zend_class_entry *scope) /* {{{ */ -{ - if (scope) { - zend_throw_error(NULL, "Call to %s %s::__construct() from scope %s", - zend_visibility_string(constructor->common.fn_flags), - ZSTR_VAL(constructor->common.scope->name), - ZSTR_VAL(scope->name) - ); - } else { - zend_throw_error(NULL, "Call to %s %s::__construct() from global scope", - zend_visibility_string(constructor->common.fn_flags), - ZSTR_VAL(constructor->common.scope->name) - ); - } -} -/* }}} */ - ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ { - zend_function *constructor = zobj->ce->constructor; - - if (constructor) { - if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) { - const zend_class_entry *scope = get_fake_or_executed_scope(); - ZEND_ASSERT(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)); - if (!zend_check_method_accessible(constructor, scope)) { - zend_bad_constructor_call(constructor, scope); - zend_object_store_ctor_failed(zobj); - constructor = NULL; - } - } - } - - return constructor; + return NULL; } /* }}} */ diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 537cad8a3644..6c1d8233a0c9 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -19,9 +19,9 @@ #include "zend.h" #include "zend_globals.h" -#include "zend_variables.h" #include "zend_API.h" #include "zend_objects_API.h" +#include "zend_attributes.h" #include "zend_fibers.h" ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size) @@ -211,3 +211,70 @@ ZEND_API ZEND_COLD zend_property_info *zend_get_property_info_for_slot_slow(zend } ZEND_HASH_FOREACH_END(); return NULL; } + +ZEND_API ZEND_COLD zend_never_inline void zend_cannot_instantiate_class_ex( + const zend_class_entry *ce, + const zend_class_entry *scope, + zend_class_entry *throwable_ce +) { + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_throw_error(throwable_ce, "Cannot instantiate interface %s", ZSTR_VAL(ce->name)); + return; + } else if (ce->ce_flags & ZEND_ACC_TRAIT) { + zend_throw_error(throwable_ce, "Cannot instantiate trait %s", ZSTR_VAL(ce->name)); + return; + } else if (ce->ce_flags & ZEND_ACC_ENUM) { + zend_throw_error(throwable_ce, "Cannot instantiate enum %s", ZSTR_VAL(ce->name)); + return; + } else if (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { + zend_throw_error(throwable_ce, "Cannot instantiate abstract class %s", ZSTR_VAL(ce->name)); + return; + } + ZEND_ASSERT(ce->constructor); + const zend_function *constructor = ce->constructor; + if (zend_is_non_instantiable_constructor(constructor)) { + const zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); + ZEND_ASSERT(non_instantiable_class); + zend_string *msg = Z_STR(non_instantiable_class->args[0].value); + /* Use zend_throw_exception_zstr() when exposed */ + zend_throw_error(throwable_ce, "%s", ZSTR_VAL(msg)); + } else if (scope) { + zend_throw_error(throwable_ce, "Call to %s %s::__construct() from scope %s", + zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name), + ZSTR_VAL(scope->name) + ); + } else { + zend_throw_error( + throwable_ce, + "Call to %s %s::__construct() from global scope", + zend_visibility_string(constructor->common.fn_flags), + ZSTR_VAL(constructor->common.scope->name) + ); + } +} + +ZEND_API bool zend_check_class_is_instantiable_or_throw(const zend_class_entry *ce, const zend_class_entry *scope) { + const zend_function *constructor = ce->constructor; + + if (UNEXPECTED(ce->ce_flags & ZEND_ACC_UNINSTANTIABLE)) { + zend_cannot_instantiate_class(ce, scope); + return false; + } + if (constructor) { + /* We cannot rely on the zend_check_method_accessible() call below as the fake non_instantiable_constructor, + * doesn't have a scope, and therefore in the global scope fn->common.scope != scope + * would be false, thus returning that the method is accessible, which is not what we need. */ + if (UNEXPECTED(zend_is_non_instantiable_constructor(constructor))) { + zend_cannot_instantiate_class(ce, NULL); + return false; + } + if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) { + if (!zend_check_method_accessible(constructor, scope)) { + zend_cannot_instantiate_class(ce, scope); + return false; + } + } + } + return true; +} + diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 2c59e3455f19..93949d33f590 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -148,24 +148,31 @@ static zend_always_inline bool zend_check_method_accessible(const zend_function return true; } -static inline zend_function *zend_get_public_constructor(const zend_class_entry *ce) { - if (UNEXPECTED(ce->constructor == NULL)) { - return NULL; - } - if (EXPECTED(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { - return ce->constructor; - } - return NULL; +static inline bool zend_is_class_instantiable(const zend_class_entry *ce) { + return + !(ce->ce_flags & ZEND_ACC_UNINSTANTIABLE) + && (!ce->constructor || ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC) + ; +} +static inline bool zend_is_class_instantiable_ignoring_ctor_visibility(const zend_class_entry *ce) { + return + !(ce->ce_flags & ZEND_ACC_UNINSTANTIABLE) + && (!ce->constructor || !zend_is_non_instantiable_constructor(ce->constructor)) + ; } -static inline zend_function *zend_get_accessible_constructor_in_scope(const zend_class_entry *ce, const zend_class_entry *scope) { - if (UNEXPECTED(ce->constructor == NULL)) { - return NULL; - } - if (EXPECTED(zend_check_method_accessible(ce->constructor, scope))) { - return ce->constructor; - } - return NULL; +ZEND_API ZEND_COLD zend_never_inline void zend_cannot_instantiate_class_ex( + const zend_class_entry *ce, + const zend_class_entry *scope, + zend_class_entry *throwable_ce +); +static inline void zend_cannot_instantiate_class( + const zend_class_entry *ce, + const zend_class_entry *scope +) { + zend_cannot_instantiate_class_ex(ce, scope, NULL); } +ZEND_API bool zend_check_class_is_instantiable_or_throw(const zend_class_entry *ce, const zend_class_entry *scope); + #endif /* ZEND_OBJECTS_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 701140dedbdb..7e80750bea92 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3847,7 +3847,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, FREE_OP2(); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -6002,24 +6002,19 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -6032,7 +6027,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a51c97c2b694..3fd6e1b214df 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7628,7 +7628,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -10357,7 +10357,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -11149,7 +11149,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -11395,24 +11395,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_CONS } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -11425,7 +11420,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_CONS /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -12982,7 +12977,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -25614,7 +25609,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -28309,7 +28304,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -29514,7 +29509,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -30082,24 +30077,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_VAR_ } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -30112,7 +30102,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_VAR_ /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -32196,7 +32186,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -34408,7 +34398,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -36490,7 +36480,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -36912,7 +36902,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -37138,24 +37128,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUS } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -37168,7 +37153,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUS /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -39086,7 +39071,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_STATIC_M } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -60335,7 +60320,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -63064,7 +63049,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -63754,7 +63739,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -64000,24 +63985,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_CONST_UNU } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -64030,7 +64010,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_CONST_UNU /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -65587,7 +65567,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -78119,7 +78099,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -80814,7 +80794,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -82019,7 +81999,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -82587,24 +82567,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_VAR_UNUSE } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -82617,7 +82592,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_VAR_UNUSE /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -84701,7 +84676,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -86913,7 +86888,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -88995,7 +88970,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -89417,7 +89392,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } @@ -89643,24 +89618,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UN } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + const zend_class_entry *scope = EX(func)->op_array.scope; + if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(ce, scope))) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); - if (UNEXPECTED(constructor == NULL)) { - /* No constructor implies that an internal get_constructor was overwritten and threw an exception. */ - if (UNEXPECTED(!EG(exception))) { - zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - ZEND_ASSERT(non_instantiable_class); - zend_string *msg = Z_STR(non_instantiable_class->args[0].value); - zend_throw_error(NULL, "%s", ZSTR_VAL(msg)); - } + if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } - /* Pass function is special */ - else if (zend_is_pass_function(constructor)) { + + constructor = ce->constructor; + if (constructor == NULL) { /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { @@ -89673,7 +89643,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UN /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION, constructor, + ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { @@ -91591,7 +91561,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_STATIC_METHOD } } else { - if (UNEXPECTED(ce->constructor == NULL || zend_is_pass_function(ce->constructor))) { + if (UNEXPECTED(ce->constructor == NULL)) { zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } diff --git a/build/gen_stub.php b/build/gen_stub.php index 2bebd4e0bf37..d9fb8ae56626 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3564,7 +3564,7 @@ function (Name $item) { } if (!$isInstantiable) { - $code .= "\n\tclass_entry->constructor = NULL;\n"; + $code .= "\n\tclass_entry->constructor = (zend_function *) &zend_non_instantiable_constructor;\n"; } $code .= "\n\treturn class_entry;\n"; diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index e9c6e46884ce..6a4415e26804 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -23,6 +23,7 @@ #include "ext/standard/info.h" #include "php_com_dotnet.h" #include "php_com_dotnet_internal.h" +#include "Zend/zend_attributes.h" #include "Zend/zend_exceptions.h" #include "Zend/zend_interfaces.h" diff --git a/ext/com_dotnet/com_extension.stub.php b/ext/com_dotnet/com_extension.stub.php index 0bbe976279f6..3144d01281ed 100644 --- a/ext/com_dotnet/com_extension.stub.php +++ b/ext/com_dotnet/com_extension.stub.php @@ -370,6 +370,7 @@ public function __construct(string $assembly_name, string $datatype_name, int $c } #endif +#[\NonInstantiableClass("Cannot directly construct com_safeproxy_array; it is for internal usage only")] final class com_safearray_proxy { } diff --git a/ext/com_dotnet/com_extension_arginfo.h b/ext/com_dotnet/com_extension_arginfo.h index d0fcf6645717..d5c165a3b42d 100644 --- a/ext/com_dotnet/com_extension_arginfo.h +++ b/ext/com_dotnet/com_extension_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit com_extension.stub.php instead. - * Stub hash: 9b2eea541946c291eb002ee98997f3dcad8bdfce */ + * Stub hash: e61fc06146db8eba3c274b4c9fa2b3202e13528e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_variant_set, 0, 2, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, variant, variant, 0) @@ -324,6 +324,14 @@ static zend_class_entry *register_class_com_safearray_proxy(void) INIT_CLASS_ENTRY(ce, "com_safearray_proxy", NULL); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + zend_string *attribute_name_NonInstantiableClass_class_com_safearray_proxy_0 = zend_string_init_interned("NonInstantiableClass", sizeof("NonInstantiableClass") - 1, true); + zend_attribute *attribute_NonInstantiableClass_class_com_safearray_proxy_0 = zend_add_class_attribute(class_entry, attribute_name_NonInstantiableClass_class_com_safearray_proxy_0, 1); + zend_string_release_ex(attribute_name_NonInstantiableClass_class_com_safearray_proxy_0, true); + zend_string *attribute_NonInstantiableClass_class_com_safearray_proxy_0_arg0_str = zend_string_init("Cannot directly construct com_safeproxy_array; it is for internal usage only", strlen("Cannot directly construct com_safeproxy_array; it is for internal usage only"), 1); + ZVAL_STR(&attribute_NonInstantiableClass_class_com_safearray_proxy_0->args[0].value, attribute_NonInstantiableClass_class_com_safearray_proxy_0_arg0_str); + + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; + return class_entry; } diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c index 0317c329589e..c920b3715a93 100644 --- a/ext/com_dotnet/com_saproxy.c +++ b/ext/com_dotnet/com_saproxy.c @@ -321,12 +321,6 @@ static zend_function *saproxy_method_get(zend_object **object, zend_string *name return NULL; } -static zend_function *saproxy_constructor_get(zend_object *object) -{ - zend_throw_error(NULL, "Cannot directly construct com_safeproxy_array; it is for internal usage only"); - return NULL; -} - static zend_string* saproxy_class_name_get(const zend_object *object) { return zend_string_copy(php_com_saproxy_class_entry->name); @@ -412,7 +406,6 @@ zend_object_handlers php_com_saproxy_handlers = { saproxy_dimension_delete, saproxy_properties_get, saproxy_method_get, - saproxy_constructor_get, saproxy_class_name_get, saproxy_object_cast, saproxy_count_elements, diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index 32fd03bdd094..b69f915714e2 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -1020,7 +1020,7 @@ static zend_class_entry *register_class_CurlHandle(void) zend_string *attribute_NonInstantiableClass_class_CurlHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlHandle, use curl_init() instead", strlen("Cannot directly construct CurlHandle, use curl_init() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_CurlHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlHandle_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1038,7 +1038,7 @@ static zend_class_entry *register_class_CurlMultiHandle(void) zend_string *attribute_NonInstantiableClass_class_CurlMultiHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlMultiHandle, use curl_multi_init() instead", strlen("Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_CurlMultiHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlMultiHandle_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1056,7 +1056,7 @@ static zend_class_entry *register_class_CurlShareHandle(void) zend_string *attribute_NonInstantiableClass_class_CurlShareHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlShareHandle, use curl_share_init() instead", strlen("Cannot directly construct CurlShareHandle, use curl_share_init() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_CurlShareHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlShareHandle_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1080,7 +1080,7 @@ static zend_class_entry *register_class_CurlSharePersistentHandle(void) zend_string *attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0_arg0_str = zend_string_init("Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead", strlen("Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0->args[0].value, attribute_NonInstantiableClass_class_CurlSharePersistentHandle_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/dba/dba_arginfo.h b/ext/dba/dba_arginfo.h index 31c3c994d5d3..8a23461601a4 100644 --- a/ext/dba/dba_arginfo.h +++ b/ext/dba/dba_arginfo.h @@ -116,7 +116,7 @@ static zend_class_entry *register_class_Dba_Connection(void) zend_string *attribute_NonInstantiableClass_class_Dba_Connection_0_arg0_str = zend_string_init("Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead", strlen("Cannot directly construct Dba\\Connection, use dba_open() or dba_popen() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Dba_Connection_0->args[0].value, attribute_NonInstantiableClass_class_Dba_Connection_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/ffi/ffi_arginfo.h b/ext/ffi/ffi_arginfo.h index 82f74a51cbca..b46cc23b8045 100644 --- a/ext/ffi/ffi_arginfo.h +++ b/ext/ffi/ffi_arginfo.h @@ -215,7 +215,7 @@ static zend_class_entry *register_class_FFI(void) zend_string *attribute_NonInstantiableClass_class_FFI_0_arg0_str = zend_string_init("Instantiation of FFI is not allowed", strlen("Instantiation of FFI is not allowed"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_0->args[0].value, attribute_NonInstantiableClass_class_FFI_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -233,7 +233,7 @@ static zend_class_entry *register_class_FFI_CData(void) zend_string *attribute_NonInstantiableClass_class_FFI_CData_0_arg0_str = zend_string_init("Instantiation of FFI\\CData is not allowed", strlen("Instantiation of FFI\\CData is not allowed"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_CData_0->args[0].value, attribute_NonInstantiableClass_class_FFI_CData_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -481,7 +481,7 @@ static zend_class_entry *register_class_FFI_CType(void) zend_string *attribute_NonInstantiableClass_class_FFI_CType_0_arg0_str = zend_string_init("Instantiation of FFI\\CType is not allowed", strlen("Instantiation of FFI\\CType is not allowed"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_FFI_CType_0->args[0].value, attribute_NonInstantiableClass_class_FFI_CType_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/ftp/ftp_arginfo.h b/ext/ftp/ftp_arginfo.h index b35cb83254a3..490d061aed50 100644 --- a/ext/ftp/ftp_arginfo.h +++ b/ext/ftp/ftp_arginfo.h @@ -304,7 +304,7 @@ static zend_class_entry *register_class_FTP_Connection(void) zend_string *attribute_NonInstantiableClass_class_FTP_Connection_0_arg0_str = zend_string_init("Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead", strlen("Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_FTP_Connection_0->args[0].value, attribute_NonInstantiableClass_class_FTP_Connection_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/gd/gd_arginfo.h b/ext/gd/gd_arginfo.h index 77006fe1574b..8c0d5ff7e825 100644 --- a/ext/gd/gd_arginfo.h +++ b/ext/gd/gd_arginfo.h @@ -946,7 +946,7 @@ static zend_class_entry *register_class_GdImage(void) zend_string *attribute_NonInstantiableClass_class_GdImage_0_arg0_str = zend_string_init("Cannot directly construct GdImage, use an appropriate image* function instead", strlen("Cannot directly construct GdImage, use an appropriate image* function instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_GdImage_0->args[0].value, attribute_NonInstantiableClass_class_GdImage_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -964,7 +964,7 @@ static zend_class_entry *register_class_GdFont(void) zend_string *attribute_NonInstantiableClass_class_GdFont_0_arg0_str = zend_string_init("Cannot directly construct GdFont, use imageloadfont() instead", strlen("Cannot directly construct GdFont, use imageloadfont() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_GdFont_0->args[0].value, attribute_NonInstantiableClass_class_GdFont_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/ldap/ldap_arginfo.h b/ext/ldap/ldap_arginfo.h index 2728c68e8a06..eb6621a48189 100644 --- a/ext/ldap/ldap_arginfo.h +++ b/ext/ldap/ldap_arginfo.h @@ -781,7 +781,7 @@ static zend_class_entry *register_class_LDAP_Connection(void) zend_string *attribute_NonInstantiableClass_class_LDAP_Connection_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\Connection, use ldap_connect() instead", strlen("Cannot directly construct LDAP\\Connection, use ldap_connect() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_Connection_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_Connection_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -799,7 +799,7 @@ static zend_class_entry *register_class_LDAP_Result(void) zend_string *attribute_NonInstantiableClass_class_LDAP_Result_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\Result, use the dedicated functions instead", strlen("Cannot directly construct LDAP\\Result, use the dedicated functions instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_Result_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_Result_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -817,7 +817,7 @@ static zend_class_entry *register_class_LDAP_ResultEntry(void) zend_string *attribute_NonInstantiableClass_class_LDAP_ResultEntry_0_arg0_str = zend_string_init("Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead", strlen("Cannot directly construct LDAP\\ResultEntry, use the dedicated functions instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_LDAP_ResultEntry_0->args[0].value, attribute_NonInstantiableClass_class_LDAP_ResultEntry_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 3826584f5218..980ba64049fc 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -761,12 +761,11 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags if (ce == NULL) { ce = zend_standard_class_def; } - /* TODO: should we only allow public constructors? */ - if (UNEXPECTED(ce->constructor == NULL || ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) { - zend_throw_error(NULL, "Class %s cannot be instantiated", ZSTR_VAL(ce->name)); + if (UNEXPECTED(!zend_is_class_instantiable(ce))) { + zend_argument_value_error(ERROR_ARG_POS(2), "Class \"%s\" cannot be instantiated", ZSTR_VAL(ce->name)); RETURN_THROWS(); } - if (zend_is_pass_function(ce->constructor) && ctor_params && zend_hash_num_elements(ctor_params) > 0) { + if (!ce->constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0) { zend_argument_value_error(ERROR_ARG_POS(3), "must be empty when the specified class (%s) does not have a constructor", ZSTR_VAL(ce->name) @@ -811,7 +810,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags zend_array_release(prop_table); } - if (!zend_is_pass_function(ce->constructor)) { + if (ce->constructor) { zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); } diff --git a/ext/mysqli/tests/bug74968.phpt b/ext/mysqli/tests/bug74968.phpt index 851e2922682d..e87450dacbe6 100644 --- a/ext/mysqli/tests/bug74968.phpt +++ b/ext/mysqli/tests/bug74968.phpt @@ -18,7 +18,7 @@ require_once 'skipifconnectfailure.inc'; ?> ==DONE== --EXPECTF-- -Fatal error: Uncaught Error: Class test cannot be instantiated in %sbug74968.php:%d +Fatal error: Uncaught ValueError: mysqli_result::fetch_object(): Argument #1 ($class) Class "test" cannot be instantiated in %sbug74968.php:%d Stack trace: #0 %sbug74968.php(%d): mysqli_result->fetch_object('test') #1 {main} diff --git a/ext/mysqli/tests/mysqli_fetch_object.phpt b/ext/mysqli/tests/mysqli_fetch_object.phpt index 28e945db52f6..6a57dd0c1ab3 100644 --- a/ext/mysqli/tests/mysqli_fetch_object.phpt +++ b/ext/mysqli/tests/mysqli_fetch_object.phpt @@ -116,14 +116,13 @@ require_once 'skipifconnectfailure.inc'; $this->b = $b; } } - /* - TODO - I think we should bail out here. The following line will give a Fatal error: Call to private ... from invalid context - var_dump($obj = new mysqli_fetch_object_private_constructor(1, 2)); - This does not fail. - */ - mysqli_fetch_object($res, 'mysqli_fetch_object_private_constructor', array('a', 'b')); - mysqli_free_result($res); + + try { + var_dump(mysqli_fetch_object($res, 'mysqli_fetch_object_private_constructor', ['a', 'b'])); + mysqli_free_result($res); + } catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; + } try { var_dump(mysqli_fetch_object($res, 'this_class_does_not_exist')); @@ -146,5 +145,6 @@ NULL NULL mysqli_result object is already closed TypeError: mysqli_fetch_object(): Argument #3 ($constructor_args) must be of type array, string given +ValueError: mysqli_fetch_object(): Argument #2 ($class) Class "mysqli_fetch_object_private_constructor" cannot be instantiated TypeError: mysqli_fetch_object(): Argument #2 ($class) must be a valid class name, this_class_does_not_exist given done! diff --git a/ext/odbc/odbc_arginfo.h b/ext/odbc/odbc_arginfo.h index 5e94bc33abd9..612f68d06d64 100644 --- a/ext/odbc/odbc_arginfo.h +++ b/ext/odbc/odbc_arginfo.h @@ -414,7 +414,7 @@ static zend_class_entry *register_class_Odbc_Connection(void) zend_string *attribute_NonInstantiableClass_class_Odbc_Connection_0_arg0_str = zend_string_init("Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead", strlen("Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Odbc_Connection_0->args[0].value, attribute_NonInstantiableClass_class_Odbc_Connection_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -432,7 +432,7 @@ static zend_class_entry *register_class_Odbc_Result(void) zend_string *attribute_NonInstantiableClass_class_Odbc_Result_0_arg0_str = zend_string_init("Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead", strlen("Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Odbc_Result_0->args[0].value, attribute_NonInstantiableClass_class_Odbc_Result_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 6acc8bc97621..9bf8ee18897d 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -929,7 +929,7 @@ static void zend_file_cache_serialize_class(zval *zv, } } - if (ce->constructor && !zend_is_pass_function(ce->constructor)) { + if (ce->constructor && !zend_is_non_instantiable_constructor(ce->constructor)) { SERIALIZE_PTR(ce->constructor); } SERIALIZE_PTR(ce->destructor); @@ -1809,8 +1809,8 @@ static void zend_file_cache_unserialize_class(zval *zv, UNSERIALIZE_PTR(ce->constructor); if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) { zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass")); - if (!non_instantiable_class) { - ce->constructor = (zend_function *) &zend_pass_function; + if (non_instantiable_class) { + ce->constructor = (zend_function *) &zend_non_instantiable_constructor;; } } UNSERIALIZE_PTR(ce->destructor); diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 872186ad502c..8eb9cdb19470 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -884,7 +884,7 @@ static zend_class_entry *register_class_Openssl_Session(void) zend_string *attribute_NonInstantiableClass_class_Openssl_Session_0_arg0_str = zend_string_init("Cannot directly construct Openssl\\Session, use Openssl\\Session::import() or TLS session callbacks", strlen("Cannot directly construct Openssl\\Session, use Openssl\\Session::import() or TLS session callbacks"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Openssl_Session_0->args[0].value, attribute_NonInstantiableClass_class_Openssl_Session_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -902,7 +902,7 @@ static zend_class_entry *register_class_OpenSSLCertificate(void) zend_string *attribute_NonInstantiableClass_class_OpenSSLCertificate_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead", strlen("Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLCertificate_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLCertificate_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -920,7 +920,7 @@ static zend_class_entry *register_class_OpenSSLCertificateSigningRequest(void) zend_string *attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead", strlen("Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLCertificateSigningRequest_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -938,7 +938,7 @@ static zend_class_entry *register_class_OpenSSLAsymmetricKey(void) zend_string *attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0_arg0_str = zend_string_init("Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead", strlen("Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0->args[0].value, attribute_NonInstantiableClass_class_OpenSSLAsymmetricKey_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 7669134aeee2..6af8bcb65372 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -626,15 +626,12 @@ PHP_METHOD(PDO, prepare) zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); RETURN_THROWS(); } - if (UNEXPECTED(dbstmt_ce->constructor == NULL)) { - zend_throw_error( - NULL, - "Class %s cannot be used as a user-supplied statement class as it cannot be instantiated", - ZSTR_VAL(dbstmt_ce->name)); + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(dbstmt_ce))) { + zend_cannot_instantiate_class(dbstmt_ce, NULL); RETURN_THROWS(); } /* Ignore default constructor as it will always be public */ - if (!zend_is_pass_function(dbstmt_ce->constructor) && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { + if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { zend_type_error("User-supplied statement class cannot have a public constructor"); RETURN_THROWS(); } @@ -930,8 +927,12 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value, u zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); return false; } + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(pce))) { + zend_cannot_instantiate_class(pce, NULL); + return false; + } /* Ignore default constructor as it will always be public */ - if (pce->constructor && !zend_is_pass_function(pce->constructor) && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { + if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { zend_argument_type_error(value_arg_num, "User-supplied statement class cannot have a public constructor"); return false; } diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index d83ee24e8795..f5b184942312 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -1088,12 +1088,12 @@ PHP_METHOD(PDOStatement, fetchObject) ce = zend_standard_class_def; } - if (UNEXPECTED(ce->constructor == NULL)) { - zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(ce->name)); + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(ce))) { + zend_argument_value_error(1, "Class \"%s\" cannot be instantiated", ZSTR_VAL(ce->name)); RETURN_THROWS(); } - if (ctor_args && zend_hash_num_elements(ctor_args) && zend_is_pass_function(ce->constructor)) { + if (ctor_args && zend_hash_num_elements(ctor_args) && ce->constructor == NULL) { zend_argument_value_error(2, "must be empty when class provided in argument #1 ($class) does not have a constructor"); RETURN_THROWS(); } @@ -1196,13 +1196,13 @@ PHP_METHOD(PDOStatement, fetchAll) } else { fetch_class = zend_standard_class_def; } - if (UNEXPECTED(fetch_class->constructor == NULL)) { + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(fetch_class))) { zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(fetch_class->name)); RETURN_THROWS(); } if (ctor_args && zend_hash_num_elements(ctor_args) > 0) { - if (zend_is_pass_function(fetch_class->constructor)) { + if (fetch_class->constructor == NULL) { zend_argument_value_error(3, "must be empty when class provided in argument #2 ($class) does not have a constructor"); RETURN_THROWS(); } @@ -1716,7 +1716,7 @@ bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_a zend_argument_type_error(arg1_arg_num, "must be a valid class"); return false; } - if (UNEXPECTED(cep->constructor == NULL)) { + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(cep))) { zend_throw_error(NULL, "Cannot instantiate an object of class %s", ZSTR_VAL(cep->name)); return false; } @@ -1729,7 +1729,7 @@ bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_a return false; } if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) { - if (zend_is_pass_function(cep->constructor)) { + if (UNEXPECTED(cep->constructor == NULL)) { zend_argument_value_error(3, "must be empty when class provided in argument #2 ($class) does not have a constructor"); return false; } diff --git a/ext/pdo/pdo_stmt_arginfo.h b/ext/pdo/pdo_stmt_arginfo.h index 789bfd69ab98..e2df00cea1d7 100644 --- a/ext/pdo/pdo_stmt_arginfo.h +++ b/ext/pdo/pdo_stmt_arginfo.h @@ -168,7 +168,7 @@ static zend_class_entry *register_class_PDORow(void) zend_string *attribute_NonInstantiableClass_class_PDORow_0_arg0_str = zend_string_init("A PDORow class cannot be manually instantiated", strlen("A PDORow class cannot be manually instantiated"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_PDORow_0->args[0].value, attribute_NonInstantiableClass_class_PDORow_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_with_abstract_class.phpt b/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_with_abstract_class.phpt index 339f4ec0be58..049d094462ed 100644 --- a/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_with_abstract_class.phpt +++ b/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_with_abstract_class.phpt @@ -38,5 +38,4 @@ $db = PDOTest::factory(); PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_abstract"); ?> --EXPECT-- -bool(true) Error: Cannot instantiate abstract class DerivedButAbstract diff --git a/ext/pdo/tests/pdo_036.phpt b/ext/pdo/tests/pdo_036.phpt index 0ca74fa98a63..9e745a3ac622 100644 --- a/ext/pdo/tests/pdo_036.phpt +++ b/ext/pdo/tests/pdo_036.phpt @@ -36,7 +36,7 @@ object(PDOStatement)#2 (1) { } Property queryString is read only -Fatal error: Uncaught ReflectionException: Class PDORow cannot be instantiated manually in %s:%d +Fatal error: Uncaught ReflectionException: A PDORow class cannot be manually instantiated in %s:%d Stack trace: #0 %spdo_036.php(%d): ReflectionClass->newInstance() #1 {main} diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 8026dd82e67a..1d6dbca0b5b2 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -2111,10 +2111,16 @@ PHP_FUNCTION(pg_fetch_object) ce = zend_standard_class_def; } - /* TODO: Should only public constructors be allowed? */ - zend_function *constructor = zend_get_accessible_constructor_in_scope(ce, ce); - if (UNEXPECTED(ce->constructor == NULL)) { - zend_argument_value_error(3, "must be an instantiable class"); + if (UNEXPECTED(!zend_is_class_instantiable(ce))) { + zend_argument_value_error(3, "Class \"%s\" cannot be instantiated", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (UNEXPECTED(!ce->constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0)) { + zend_argument_value_error(4, + "must be empty when the specified class (%s) does not have a constructor", + ZSTR_VAL(ce->name) + ); RETURN_THROWS(); } @@ -2137,16 +2143,8 @@ PHP_FUNCTION(pg_fetch_object) } zend_object *obj = Z_OBJ_P(return_value); - if (UNEXPECTED(zend_is_pass_function(constructor) && ctor_params && zend_hash_num_elements(ctor_params) > 0)) { - zend_argument_value_error(4, - "must be empty when the specified class (%s) does not have a constructor", - ZSTR_VAL(ce->name) - ); - RETURN_THROWS(); - } - - if (!zend_is_pass_function(constructor)) { - zend_call_known_function(constructor, obj, ce, + if (ce->constructor) { + zend_call_known_function(ce->constructor, obj, ce, /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); if (EG(exception)) { zend_object_store_ctor_failed(obj); diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index cc4df70e92ca..4d5451d62b20 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1037,7 +1037,7 @@ static zend_class_entry *register_class_PgSql_Connection(void) zend_string *attribute_NonInstantiableClass_class_PgSql_Connection_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead", strlen("Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Connection_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Connection_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1055,7 +1055,7 @@ static zend_class_entry *register_class_PgSql_Result(void) zend_string *attribute_NonInstantiableClass_class_PgSql_Result_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Result, use a dedicated function instead", strlen("Cannot directly construct PgSql\\Result, use a dedicated function instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Result_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Result_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1073,7 +1073,7 @@ static zend_class_entry *register_class_PgSql_Lob(void) zend_string *attribute_NonInstantiableClass_class_PgSql_Lob_0_arg0_str = zend_string_init("Cannot directly construct PgSql\\Connection, use pg_lo_open() instead", strlen("Cannot directly construct PgSql\\Connection, use pg_lo_open() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_PgSql_Lob_0->args[0].value, attribute_NonInstantiableClass_class_PgSql_Lob_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt b/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt index afd295bf28d7..6dfb668834bc 100644 --- a/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt +++ b/ext/pgsql/tests/pg_fetch_object_with_abstract_class.phpt @@ -54,6 +54,6 @@ $db = @pg_connect($conn_str); @pg_query($db, "DROP TABLE IF EXISTS pg_fetch_object_abstract_class cascade"); ?> --EXPECT-- -ValueError: pg_fetch_object(): Argument #3 ($class) must be an instantiable class -Error: Cannot instantiate abstract class C -ValueError: pg_fetch_object(): Argument #3 ($class) must be an instantiable class +ValueError: pg_fetch_object(): Argument #3 ($class) Class "I" cannot be instantiated +ValueError: pg_fetch_object(): Argument #3 ($class) Class "C" cannot be instantiated +ValueError: pg_fetch_object(): Argument #3 ($class) Class "E" cannot be instantiated diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8442435d0986..3bb59f9c81bb 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4451,7 +4451,7 @@ ZEND_METHOD(ReflectionClass, getConstructor) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - if (ce->constructor && !zend_is_pass_function(ce->constructor)) { + if (ce->constructor && !zend_is_non_instantiable_constructor(ce->constructor)) { reflection_method_factory(ce, ce->constructor, NULL, return_value); } else { RETURN_NULL(); @@ -4517,10 +4517,6 @@ ZEND_METHOD(ReflectionClass, getMethod) /* {{{ _addmethod */ static bool _addmethod(zend_function *mptr, zend_class_entry *ce, HashTable *ht, zend_long filter) { - /* Skip fake constructor */ - if (zend_is_pass_function(mptr)) { - return false; - } if ((mptr->common.fn_flags & ZEND_ACC_PRIVATE) && mptr->common.scope != ce) { return false; } @@ -4905,16 +4901,8 @@ ZEND_METHOD(ReflectionClass, isInstantiable) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - if (ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_ENUM)) { - RETURN_FALSE; - } - /* Classes marked with the #[\NonInstantiableClass()] attribute are not instantiable */ - if (ce->constructor == NULL) { - RETURN_FALSE; - } - - RETURN_BOOL(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC); + RETURN_BOOL(zend_is_class_instantiable(ce)); } /* }}} */ @@ -5000,6 +4988,15 @@ ZEND_METHOD(ReflectionClass, getModifiers) } /* }}} */ +static ZEND_COLD void reflection_not_instantiable_class(const zend_class_entry* ce) { + if (ce->ce_flags & ZEND_ACC_UNINSTANTIABLE + || (ce->constructor && zend_is_non_instantiable_constructor(ce->constructor))) { + zend_cannot_instantiate_class_ex(ce, NULL, reflection_exception_ptr); + } else { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); + } +} + /* {{{ Returns whether the given object is an instance of this class */ ZEND_METHOD(ReflectionClass, isInstance) { @@ -5023,14 +5020,8 @@ ZEND_METHOD(ReflectionClass, newInstance) GET_REFLECTION_OBJECT_PTR(ce); - zend_function *constructor = zend_get_public_constructor(ce); - if (UNEXPECTED(constructor == NULL)) { - if (ce->constructor == NULL) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); - } else { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); - } + if (UNEXPECTED(!zend_is_class_instantiable(ce))) { + reflection_not_instantiable_class(ce); RETURN_THROWS(); } @@ -5048,9 +5039,9 @@ ZEND_METHOD(ReflectionClass, newInstance) ZEND_PARSE_PARAMETERS_END(); /* Run the constructor if there is one */ - if (!zend_is_pass_function(constructor)) { + if (ce->constructor) { zend_call_known_function( - constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, + ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, num_args, params, named_params); if (EG(exception)) { @@ -5070,9 +5061,8 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor) ZEND_PARSE_PARAMETERS_NONE(); - if (UNEXPECTED(ce->constructor == NULL)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); + if (UNEXPECTED(!zend_is_class_instantiable_ignoring_ctor_visibility(ce))) { + zend_cannot_instantiate_class_ex(ce, NULL, reflection_exception_ptr); RETURN_THROWS(); } @@ -5099,25 +5089,19 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs) RETURN_THROWS(); } - zend_function *constructor = zend_get_public_constructor(ce); - if (UNEXPECTED(constructor == NULL)) { - if (ce->constructor == NULL) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Class %s cannot be instantiated manually", ZSTR_VAL(ce->name)); - } else { - zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name)); - } + if (UNEXPECTED(!zend_is_class_instantiable(ce))) { + reflection_not_instantiable_class(ce); RETURN_THROWS(); } if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) { - return; + RETURN_THROWS(); } /* Run the constructor if there is one */ - if (!zend_is_pass_function(constructor)) { + if (ce->constructor) { zend_call_known_function( - constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args); + ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args); if (EG(exception)) { zend_object_store_ctor_failed(Z_OBJ_P(return_value)); diff --git a/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt b/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt index 14e51c499082..835c91a1ab7f 100644 --- a/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt +++ b/ext/reflection/tests/ReflectionClass_newInstanceWithoutConstructor.phpt @@ -42,7 +42,7 @@ object(stdClass)#%d (0) { } object(DateTime)#%d (0) { } -Class Generator cannot be instantiated manually +The "Generator" class is reserved for internal use and cannot be manually instantiated object(Bar)#%d (1) { ["storage":"ArrayObject":private]=> array(0) { diff --git a/ext/reflection/tests/bug64007.phpt b/ext/reflection/tests/bug64007.phpt index eabb7a7c5ca4..81855d914441 100644 --- a/ext/reflection/tests/bug64007.phpt +++ b/ext/reflection/tests/bug64007.phpt @@ -6,18 +6,17 @@ $reflection = new ReflectionClass('Generator'); try { $generator = $reflection->newInstanceWithoutConstructor(); var_dump($generator); -} catch (Exception $e) { - var_dump($e->getMessage()); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; } -$generator = $reflection->newInstance(); -var_dump($generator); +try { + $generator = $reflection->newInstance(); + var_dump($generator); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -string(47) "Class Generator cannot be instantiated manually" - -Fatal error: Uncaught ReflectionException: Class Generator cannot be instantiated manually in %s:%d -Stack trace: -#0 %s(%d): ReflectionClass->newInstance() -#1 {main} - thrown in %s on line %d +--EXPECT-- +ReflectionException: The "Generator" class is reserved for internal use and cannot be manually instantiated +ReflectionException: The "Generator" class is reserved for internal use and cannot be manually instantiated diff --git a/ext/shmop/shmop_arginfo.h b/ext/shmop/shmop_arginfo.h index ba78fd1ebd52..7b5fdd1500ad 100644 --- a/ext/shmop/shmop_arginfo.h +++ b/ext/shmop/shmop_arginfo.h @@ -73,7 +73,7 @@ static zend_class_entry *register_class_Shmop(void) zend_string *attribute_NonInstantiableClass_class_Shmop_0_arg0_str = zend_string_init("Cannot directly construct Shmop, use shmop_open() instead", strlen("Cannot directly construct Shmop, use shmop_open() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Shmop_0->args[0].value, attribute_NonInstantiableClass_class_Shmop_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index e5186f4b6eb4..cea90882509d 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -339,7 +339,7 @@ static zend_class_entry *register_class_Soap_Url(void) zend_string *attribute_NonInstantiableClass_class_Soap_Url_0_arg0_str = zend_string_init("Cannot directly construct Soap\\Url", strlen("Cannot directly construct Soap\\Url"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Soap_Url_0->args[0].value, attribute_NonInstantiableClass_class_Soap_Url_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -357,7 +357,7 @@ static zend_class_entry *register_class_Soap_Sdl(void) zend_string *attribute_NonInstantiableClass_class_Soap_Sdl_0_arg0_str = zend_string_init("Cannot directly construct Soap\\Sdl", strlen("Cannot directly construct Soap\\Sdl"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Soap_Sdl_0->args[0].value, attribute_NonInstantiableClass_class_Soap_Sdl_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 1c978647bea7..15dade755364 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1124,7 +1124,7 @@ static zend_class_entry *register_class_Socket(void) zend_string *attribute_NonInstantiableClass_class_Socket_0_arg0_str = zend_string_init("Cannot directly construct Socket, use socket_create() instead", strlen("Cannot directly construct Socket, use socket_create() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Socket_0->args[0].value, attribute_NonInstantiableClass_class_Socket_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -1142,7 +1142,7 @@ static zend_class_entry *register_class_AddressInfo(void) zend_string *attribute_NonInstantiableClass_class_AddressInfo_0_arg0_str = zend_string_init("Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead", strlen("Cannot directly construct AddressInfo, use socket_addrinfo_lookup() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_AddressInfo_0->args[0].value, attribute_NonInstantiableClass_class_AddressInfo_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/standard/dir_arginfo.h b/ext/standard/dir_arginfo.h index 1cf196fbb5e3..2c80e17320b7 100644 --- a/ext/standard/dir_arginfo.h +++ b/ext/standard/dir_arginfo.h @@ -76,7 +76,7 @@ static zend_class_entry *register_class_Directory(void) zend_string *attribute_NonInstantiableClass_class_Directory_0_arg0_str = zend_string_init("Cannot directly construct Directory, use dir() instead", strlen("Cannot directly construct Directory, use dir() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_Directory_0->args[0].value, attribute_NonInstantiableClass_class_Directory_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt index a86868a92049..8a912bd00d22 100644 --- a/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt +++ b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt @@ -14,4 +14,4 @@ try { ?> --EXPECT-- bool(false) -ReflectionException: Class Directory cannot be instantiated manually +ReflectionException: Cannot directly construct Directory, use dir() instead diff --git a/ext/sysvmsg/sysvmsg_arginfo.h b/ext/sysvmsg/sysvmsg_arginfo.h index 95a02c1360c0..e9c731317335 100644 --- a/ext/sysvmsg/sysvmsg_arginfo.h +++ b/ext/sysvmsg/sysvmsg_arginfo.h @@ -84,7 +84,7 @@ static zend_class_entry *register_class_SysvMessageQueue(void) zend_string *attribute_NonInstantiableClass_class_SysvMessageQueue_0_arg0_str = zend_string_init("Cannot directly construct SysvMessageQueue, use msg_get_queue() instead", strlen("Cannot directly construct SysvMessageQueue, use msg_get_queue() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_SysvMessageQueue_0->args[0].value, attribute_NonInstantiableClass_class_SysvMessageQueue_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/sysvsem/sysvsem_arginfo.h b/ext/sysvsem/sysvsem_arginfo.h index 82a5fcf9c18d..ef630d3e8b75 100644 --- a/ext/sysvsem/sysvsem_arginfo.h +++ b/ext/sysvsem/sysvsem_arginfo.h @@ -45,7 +45,7 @@ static zend_class_entry *register_class_SysvSemaphore(void) zend_string *attribute_NonInstantiableClass_class_SysvSemaphore_0_arg0_str = zend_string_init("Cannot directly construct SysvSemaphore, use sem_get() instead", strlen("Cannot directly construct SysvSemaphore, use sem_get() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_SysvSemaphore_0->args[0].value, attribute_NonInstantiableClass_class_SysvSemaphore_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/sysvshm/sysvshm_arginfo.h b/ext/sysvshm/sysvshm_arginfo.h index 717769adc10d..5ddc693f2bbe 100644 --- a/ext/sysvshm/sysvshm_arginfo.h +++ b/ext/sysvshm/sysvshm_arginfo.h @@ -65,7 +65,7 @@ static zend_class_entry *register_class_SysvSharedMemory(void) zend_string *attribute_NonInstantiableClass_class_SysvSharedMemory_0_arg0_str = zend_string_init("Cannot directly construct SysvSharedMemory, use shm_attach() instead", strlen("Cannot directly construct SysvSharedMemory, use shm_attach() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_SysvSharedMemory_0->args[0].value, attribute_NonInstantiableClass_class_SysvSharedMemory_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/xml/xml_arginfo.h b/ext/xml/xml_arginfo.h index 0adcff9466ce..e515be880af5 100644 --- a/ext/xml/xml_arginfo.h +++ b/ext/xml/xml_arginfo.h @@ -191,7 +191,7 @@ static zend_class_entry *register_class_XMLParser(void) zend_string *attribute_NonInstantiableClass_class_XMLParser_0_arg0_str = zend_string_init("Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead", strlen("Cannot directly construct XMLParser, use xml_parser_create() or xml_parser_create_ns() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_XMLParser_0->args[0].value, attribute_NonInstantiableClass_class_XMLParser_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/ext/zlib/zlib_arginfo.h b/ext/zlib/zlib_arginfo.h index c479c7e1e0b6..1dd6f1410c0a 100644 --- a/ext/zlib/zlib_arginfo.h +++ b/ext/zlib/zlib_arginfo.h @@ -240,7 +240,7 @@ static zend_class_entry *register_class_InflateContext(void) zend_string *attribute_NonInstantiableClass_class_InflateContext_0_arg0_str = zend_string_init("Cannot directly construct InflateContext, use inflate_init() instead", strlen("Cannot directly construct InflateContext, use inflate_init() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_InflateContext_0->args[0].value, attribute_NonInstantiableClass_class_InflateContext_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } @@ -258,7 +258,7 @@ static zend_class_entry *register_class_DeflateContext(void) zend_string *attribute_NonInstantiableClass_class_DeflateContext_0_arg0_str = zend_string_init("Cannot directly construct DeflateContext, use deflate_init() instead", strlen("Cannot directly construct DeflateContext, use deflate_init() instead"), 1); ZVAL_STR(&attribute_NonInstantiableClass_class_DeflateContext_0->args[0].value, attribute_NonInstantiableClass_class_DeflateContext_0_arg0_str); - class_entry->constructor = NULL; + class_entry->constructor = (zend_function *) &zend_non_instantiable_constructor; return class_entry; } diff --git a/sapi/fuzzer/fuzzer-sapi.c b/sapi/fuzzer/fuzzer-sapi.c index c5bbf721ce45..fd8f364de2bf 100644 --- a/sapi/fuzzer/fuzzer-sapi.c +++ b/sapi/fuzzer/fuzzer-sapi.c @@ -131,7 +131,7 @@ static void fuzzer_disable_classes(void) /* Lowercase as this is how the CE as stored */ zend_class_entry *InfiniteIterator_class = zend_hash_str_find_ptr(CG(class_table), "infiniteiterator", strlen("infiniteiterator")); - zend_class_entry *InfiniteIterator_class->constructor = NULL; + zend_class_entry *InfiniteIterator_class->constructor = (zend_function *) &zend_non_instantiable_constructor; } int fuzzer_init_php(const char *extra_ini) From ba0452579f39b1a03766dce042ed983dfd5004f2 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 May 2026 17:26:53 +0200 Subject: [PATCH 3/3] Remove get_constructor object handler --- NEWS | 5 ++--- UPGRADING.INTERNALS | 4 ++++ Zend/Optimizer/escape_analysis.c | 2 -- Zend/Optimizer/zend_inference.c | 3 +-- Zend/zend_iterators.c | 1 - Zend/zend_object_handlers.c | 7 ------- Zend/zend_object_handlers.h | 3 --- ext/com_dotnet/com_handlers.c | 1 - 8 files changed, 7 insertions(+), 19 deletions(-) diff --git a/NEWS b/NEWS index 23212414d361..ca93eb90d640 100644 --- a/NEWS +++ b/NEWS @@ -126,9 +126,8 @@ PHP NEWS - PGSQL: . Enabled 64 bits support for pg_lo_truncate()/pg_lo_tell() if the server supports it. (KentarouTakeda) - . pg_fetch_object() now surfaces non-instantiable class errors - before fetching, resolves the constructor via the get_constructor - handler, and reports the empty-constructor ValueError on the + . pg_fetch_object() now surfaces non-instantiable class errorsv before + fetching, and reports the empty-constructor ValueError on the $constructor_args argument. (David Carlier) - Phar: diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index c340ba64833c..8cc8abd82615 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -110,6 +110,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added ZEND_CONTAINER_OF(). . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead use php_check_open_basedir() directly. + . The get_constructor object handler has been removed. + Instead to mark an internal class as not instantiable the new + #[\NonInstantiableClass("Reason")] attribute should be attached to the + class definition in the stubs. ======================== 2. Build system changes diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index 8dbd6855d68d..69fcaf35306a 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -165,7 +165,6 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i if (ce && !ce->parent && !ce->create_object - && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object && !ce->constructor && !ce->destructor @@ -234,7 +233,6 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va script, op_array, opline); if (ce && !ce->create_object - && ce->default_object_handlers->get_constructor == zend_std_get_constructor && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object && !ce->constructor && !ce->destructor diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 05d33d3d75fb..fbf7a938ed34 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3394,8 +3394,7 @@ static zend_always_inline zend_result _zend_update_type_info( /* New objects without constructors cannot escape. */ if (ce && !ce->constructor - && !ce->create_object - && ce->default_object_handlers->get_constructor == zend_std_get_constructor) { + && !ce->create_object) { tmp &= ~MAY_BE_RCN; } UPDATE_SSA_TYPE(tmp, ssa_op->result_def); diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index c05434486f89..41a2e46ca8e5 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -42,7 +42,6 @@ static const zend_object_handlers iterator_object_handlers = { NULL, /* unset dim */ NULL, /* props get */ NULL, /* method get */ - NULL, /* get ctor */ NULL, /* get class name */ NULL, /* cast */ NULL, /* count */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 65797a1af5f6..62b418e19d96 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2173,12 +2173,6 @@ ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *c } /* }}} */ -ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ -{ - return NULL; -} -/* }}} */ - ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ { zend_object *zobj1, *zobj2; @@ -2637,7 +2631,6 @@ ZEND_API const zend_object_handlers std_object_handlers = { zend_std_unset_dimension, /* unset_dimension */ zend_std_get_properties, /* get_properties */ zend_std_get_method, /* get_method */ - zend_std_get_constructor, /* get_constructor */ zend_std_get_class_name, /* get_class_name */ zend_std_cast_object_tostring, /* cast_object */ NULL, /* count_elements */ diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index d0dd804e8a41..24f9d7757a67 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -153,7 +153,6 @@ typedef zend_array *(*zend_object_get_properties_for_t)(zend_object *object, zen /* Andi - EX(fbc) (function being called) needs to be initialized already in the INIT fcall opcode so that the parameters can be parsed the right way. We need to add another callback for this. */ typedef zend_function *(*zend_object_get_method_t)(zend_object **object, zend_string *method, const zval *key); -typedef zend_function *(*zend_object_get_constructor_t)(zend_object *object); /* free_obj should release any resources the object holds, without freeing the * object structure itself. The object does not need to be in a valid state after @@ -221,7 +220,6 @@ struct _zend_object_handlers { zend_object_unset_dimension_t unset_dimension; /* required */ zend_object_get_properties_t get_properties; /* required */ zend_object_get_method_t get_method; /* required */ - zend_object_get_constructor_t get_constructor; /* required */ zend_object_get_class_name_t get_class_name; /* required */ zend_object_cast_t cast_object; /* required */ zend_object_count_elements_t count_elements; /* optional */ @@ -251,7 +249,6 @@ ZEND_API zend_function *zend_std_get_static_method(const zend_class_entry *ce, z ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend_string *property_name, int type, struct _zend_property_info **prop_info); ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type); ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name); -ZEND_API zend_function *zend_std_get_constructor(zend_object *object); ZEND_API struct _zend_property_info *zend_get_property_info(const zend_class_entry *ce, zend_string *member, int silent); ZEND_API HashTable *zend_std_get_properties(zend_object *object); ZEND_API HashTable *zend_get_properties_no_lazy_init(zend_object *zobj); diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index 81c42da34972..01f966e50cba 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -524,7 +524,6 @@ zend_object_handlers php_com_object_handlers = { com_dimension_delete, com_properties_get, com_method_get, - zend_std_get_constructor, com_class_name_get, com_object_cast, com_object_count,