Skip to content

Commit ddd472b

Browse files
committed
Use a private constructor instead
Add some APIs so that we can stop relying on the get_constructor object handler
1 parent 45500c8 commit ddd472b

58 files changed

Lines changed: 374 additions & 380 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Zend/Optimizer/escape_analysis.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
167167
&& !ce->create_object
168168
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
169169
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
170-
&& (!ce->constructor || zend_is_pass_function(ce->constructor))
170+
&& !ce->constructor
171171
&& !ce->destructor
172172
&& !ce->__get
173173
&& !ce->__set
@@ -236,7 +236,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
236236
&& !ce->create_object
237237
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
238238
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
239-
&& (!ce->constructor || zend_is_pass_function(ce->constructor))
239+
&& !ce->constructor
240240
&& !ce->destructor
241241
&& !ce->__get
242242
&& !ce->__set

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3393,7 +3393,7 @@ static zend_always_inline zend_result _zend_update_type_info(
33933393
}
33943394
/* New objects without constructors cannot escape. */
33953395
if (ce
3396-
&& (!ce->constructor || zend_is_pass_function(ce->constructor))
3396+
&& !ce->constructor
33973397
&& !ce->create_object
33983398
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor) {
33993399
tmp &= ~MAY_BE_RCN;

Zend/tests/closures/closure_instantiate.phpt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,10 @@ Mark Baker mark@lange.demon.co.uk at the PHPNW2017 Conference for PHP Testfest 2
88
try {
99
// Closures should be instantiatable using new
1010
$x = new Closure();
11-
} catch (Exception $e) {
12-
// Instantiating a closure is an error, not an exception, so we shouldn't see this
13-
echo 'EXCEPTION: ', $e->getMessage();
1411
} catch (Throwable $e) {
15-
// This is the message that we should see for a caught error
16-
echo 'ERROR: ', $e->getMessage();
12+
echo $e::class, ': ', $e->getMessage();
1713
}
1814

1915
?>
2016
--EXPECT--
21-
ERROR: Instantiation of class Closure is not allowed
17+
Error: Instantiation of class Closure is not allowed

Zend/tests/enum/no-new-through-reflection.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ try {
1313

1414
?>
1515
--EXPECT--
16-
ReflectionException: Class Foo cannot be instantiated manually
16+
ReflectionException: Cannot instantiate enum Foo

Zend/tests/traits/bug60173.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ $rc = new ReflectionClass('foo');
99
$rc->newInstance();
1010
?>
1111
--EXPECTF--
12-
Fatal error: Uncaught ReflectionException: Class foo cannot be instantiated manually in %s:%d
12+
Fatal error: Uncaught ReflectionException: Cannot instantiate trait foo in %s:%d
1313
Stack trace:
1414
#0 %s(%d): ReflectionClass->newInstance()
1515
#1 {main}

Zend/zend_API.c

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,16 +1793,7 @@ ZEND_API void object_properties_load(zend_object *object, const HashTable *prope
17931793
static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */
17941794
{
17951795
if (UNEXPECTED(class_type->ce_flags & ZEND_ACC_UNINSTANTIABLE)) {
1796-
if (class_type->ce_flags & ZEND_ACC_INTERFACE) {
1797-
zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name));
1798-
} else if (class_type->ce_flags & ZEND_ACC_TRAIT) {
1799-
zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name));
1800-
} else if (class_type->ce_flags & ZEND_ACC_ENUM) {
1801-
zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(class_type->name));
1802-
} else {
1803-
ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS));
1804-
zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name));
1805-
}
1796+
zend_cannot_instantiate_class(class_type, NULL);
18061797
ZVAL_NULL(arg);
18071798
Z_OBJ_P(arg) = NULL;
18081799
return FAILURE;
@@ -1846,25 +1837,7 @@ ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /*
18461837

18471838
ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params) /* {{{ */
18481839
{
1849-
zend_function *constructor = class_type->constructor;
1850-
if (UNEXPECTED(constructor == NULL)) {
1851-
const zend_attribute *non_instantiable_class = zend_get_attribute_str(class_type->attributes, ZEND_STRL("noninstantiableclass"));
1852-
ZEND_ASSERT(non_instantiable_class);
1853-
zend_string *msg = Z_STR(non_instantiable_class->args[0].value);
1854-
zend_throw_error(NULL, "%s", ZSTR_VAL(msg));
1855-
ZVAL_UNDEF(arg);
1856-
return FAILURE;
1857-
}
1858-
1859-
if (UNEXPECTED(!(constructor->common.fn_flags & ZEND_ACC_PUBLIC))) {
1860-
/* Use zend_bad_constructor_call() somehow? */
1861-
zend_throw_error(
1862-
NULL,
1863-
"Call to %s %s::%s() from global scope",
1864-
zend_visibility_string(class_type->constructor->common.fn_flags),
1865-
ZSTR_VAL(class_type->constructor->common.scope->name),
1866-
ZSTR_VAL(class_type->constructor->common.function_name)
1867-
);
1840+
if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(class_type, zend_get_executed_scope()))) {
18681841
ZVAL_UNDEF(arg);
18691842
return FAILURE;
18701843
}
@@ -1876,8 +1849,9 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c
18761849
}
18771850
zend_object *obj = Z_OBJ_P(arg);
18781851

1879-
/* Fake constructor means we don't need to call the constructor actually */
1880-
if (zend_is_pass_function(constructor)) {
1852+
zend_function *constructor = class_type->constructor;
1853+
/* No constructor, so no need to call it */
1854+
if (constructor == NULL) {
18811855
/* Surprisingly, this is the only case where internal classes will allow to pass extra arguments
18821856
* However, if there are named arguments (and it is not empty),
18831857
* 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
35233497
if (class_entry->info.internal.builtin_functions) {
35243498
zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type);
35253499
}
3526-
3527-
/* Assign the pass function as a default constructor */
3528-
if (class_entry->constructor == NULL) {
3529-
class_entry->constructor = (zend_function *) &zend_pass_function;
3530-
}
35313500

35323501
lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT);
35333502
lowercase_name = zend_new_interned_string(lowercase_name);

Zend/zend_ast.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,10 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10581058
return FAILURE;
10591059
}
10601060

1061+
if (!zend_is_class_instantiable(ce)) {
1062+
return FAILURE;
1063+
}
1064+
10611065
if (object_init_ex(result, ce) != SUCCESS) {
10621066
return FAILURE;
10631067
}
@@ -1096,10 +1100,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10961100
}
10971101
}
10981102

1099-
zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
1100-
if (ctor) {
1103+
if (ce->constructor) {
11011104
zend_call_known_function(
1102-
ctor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
1105+
ce->constructor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
11031106
}
11041107

11051108
zend_array_destroy(args);
@@ -1117,10 +1120,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
11171120
}
11181121
}
11191122

1120-
zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
1121-
if (ctor) {
1123+
if (ce->constructor) {
11221124
zend_call_known_instance_method(
1123-
ctor, Z_OBJ_P(result), NULL, args_ast->children, args);
1125+
ce->constructor, Z_OBJ_P(result), NULL, args_ast->children, args);
11241126
}
11251127

11261128
for (uint32_t i = 0; i < args_ast->children; i++) {

Zend/zend_closures_arginfo.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_compile.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5704,7 +5704,9 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
57045704

57055705

57065706
const zend_function *fbc = NULL;
5707-
if (ce && ce->constructor && is_func_accessible(ce->constructor)) {
5707+
if (ce
5708+
&& ce->constructor
5709+
&& is_func_accessible(ce->constructor)) {
57085710
fbc = ce->constructor;
57095711
}
57105712

@@ -9527,6 +9529,33 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
95279529
zend_type_release(type, 0);
95289530
}
95299531

9532+
static ZEND_FUNCTION(non_instantiable_constructor)
9533+
{
9534+
}
9535+
9536+
static zend_arg_info zend_non_instantiable_constructor_arg_info[1] = {0};
9537+
ZEND_API const zend_internal_function zend_non_instantiable_constructor = {
9538+
ZEND_INTERNAL_FUNCTION, /* type */
9539+
{0, 0, 0}, /* arg_flags */
9540+
ZEND_ACC_PRIVATE, /* fn_flags */
9541+
NULL, /* name */
9542+
NULL, /* scope */
9543+
NULL, /* prototype */
9544+
0, /* num_args */
9545+
0, /* required_num_args */
9546+
zend_non_instantiable_constructor_arg_info + 1, /* arg_info */
9547+
NULL, /* attributes */
9548+
NULL, /* run_time_cache */
9549+
NULL, /* doc_comment */
9550+
0, /* T */
9551+
0, /* fn_flags2 */
9552+
NULL, /* prop_info */
9553+
ZEND_FN(non_instantiable_constructor), /* handler */
9554+
NULL, /* module */
9555+
NULL, /* frameless_function_infos */
9556+
{NULL,NULL,NULL,NULL} /* reserved */
9557+
};
9558+
95309559
static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool toplevel) /* {{{ */
95319560
{
95329561
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
96489677
ce->ce_flags |= ZEND_ACC_TOP_LEVEL;
96499678
}
96509679

9651-
/* Add a default "pass" constructor if none are defined */
9680+
/* Add zend_non_instantiable_constructor constructor if class cannot be manually instantiated */
96529681
if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) {
9653-
zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass"));
9654-
if (!non_instantiable_class) {
9655-
ce->constructor = (zend_function *) &zend_pass_function;
9682+
const zend_attribute *non_instantiable_class = zend_get_attribute_str(ce->attributes, ZEND_STRL("noninstantiableclass"));
9683+
if (non_instantiable_class) {
9684+
ce->constructor = (zend_function *) &zend_non_instantiable_constructor;
96569685
}
96579686
}
96589687

Zend/zend_compile.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -654,11 +654,11 @@ struct _zend_execute_data {
654654
zend_array *extra_named_params;
655655
};
656656

657-
/* export zend_pass_function to allow comparisons against it */
658-
extern ZEND_API const zend_internal_function zend_pass_function;
657+
/* export zend_non_instantiable_constructor to allow comparisons against it */
658+
extern ZEND_API const zend_internal_function zend_non_instantiable_constructor;
659659

660-
static zend_always_inline bool zend_is_pass_function(const zend_function *fn) {
661-
return fn == (const zend_function*) &zend_pass_function;
660+
static zend_always_inline bool zend_is_non_instantiable_constructor(const zend_function *fn) {
661+
return fn == (const zend_function*) &zend_non_instantiable_constructor;
662662
}
663663

664664
#define ZEND_CALL_HAS_THIS IS_OBJECT_EX

0 commit comments

Comments
 (0)