Skip to content

Commit 2a0b003

Browse files
committed
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
1 parent 5f2ed88 commit 2a0b003

97 files changed

Lines changed: 727 additions & 453 deletions

File tree

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
170+
&& (!ce->constructor || zend_is_pass_function(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
239+
&& (!ce->constructor || zend_is_pass_function(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
3396+
&& (!ce->constructor || zend_is_pass_function(ce->constructor))
33973397
&& !ce->create_object
33983398
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor) {
33993399
tmp &= ~MAY_BE_RCN;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ enum Foo {}
77

88
try {
99
(new \ReflectionClass(Foo::class))->newInstanceWithoutConstructor();
10-
} catch (Error $e) {
11-
echo $e->getMessage() . "\n";
10+
} catch (Throwable $e) {
11+
echo $e::class, ': ', $e->getMessage() . "\n";
1212
}
1313

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

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 Error: Cannot instantiate trait foo in %s:%d
12+
Fatal error: Uncaught ReflectionException: Class foo cannot be instantiated manually in %s:%d
1313
Stack trace:
1414
#0 %s(%d): ReflectionClass->newInstance()
1515
#1 {main}

Zend/zend_API.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,6 +3510,11 @@ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig
35103510
if (class_entry->info.internal.builtin_functions) {
35113511
zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type);
35123512
}
3513+
3514+
/* Assign the pass function as a default constructor */
3515+
if (class_entry->constructor == NULL) {
3516+
class_entry->constructor = (zend_function *) &zend_pass_function;
3517+
}
35133518

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

Zend/zend_attributes.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ ZEND_API zend_class_entry *zend_ce_override;
3232
ZEND_API zend_class_entry *zend_ce_deprecated;
3333
ZEND_API zend_class_entry *zend_ce_nodiscard;
3434
ZEND_API zend_class_entry *zend_ce_delayed_target_validation;
35+
ZEND_API zend_class_entry *zend_ce_non_instantiable_class;
3536

3637
static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
3738

@@ -264,6 +265,24 @@ ZEND_METHOD(NoDiscard, __construct)
264265
}
265266
}
266267

268+
ZEND_METHOD(NonInstantiableClass, __construct)
269+
{
270+
zend_string *message = NULL;
271+
zval value;
272+
273+
ZEND_PARSE_PARAMETERS_START(1, 1)
274+
Z_PARAM_STR(message)
275+
ZEND_PARSE_PARAMETERS_END();
276+
277+
ZVAL_STR(&value, message);
278+
zend_update_property_ex(zend_ce_non_instantiable_class, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);
279+
280+
/* The assignment might fail due to 'readonly'. */
281+
if (UNEXPECTED(EG(exception))) {
282+
RETURN_THROWS();
283+
}
284+
}
285+
267286
static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset)
268287
{
269288
if (attributes) {
@@ -605,6 +624,9 @@ void zend_register_attribute_ce(void)
605624

606625
zend_ce_delayed_target_validation = register_class_DelayedTargetValidation();
607626
attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation);
627+
628+
zend_ce_non_instantiable_class = register_class_NonInstantiableClass();
629+
attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation);
608630
}
609631

610632
void zend_attributes_shutdown(void)

Zend/zend_attributes.stub.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,13 @@ public function __construct(?string $message = null) {}
103103
*/
104104
#[Attribute(Attribute::TARGET_ALL)]
105105
final class DelayedTargetValidation {}
106+
107+
/**
108+
* @strict-properties
109+
*/
110+
#[Attribute(Attribute::TARGET_CLASS)]
111+
final class NonInstantiableClass {
112+
public readonly string $message;
113+
114+
public function __construct(string $message) {}
115+
}

Zend/zend_attributes_arginfo.h

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

Zend/zend_closures.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "zend.h"
2121
#include "zend_API.h"
2222
#include "zend_closures.h"
23+
#include "zend_attributes.h"
2324
#include "zend_exceptions.h"
2425
#include "zend_interfaces.h"
2526
#include "zend_objects.h"
@@ -448,13 +449,6 @@ ZEND_METHOD(Closure, getCurrent)
448449
RETURN_OBJ_COPY(obj);
449450
}
450451

451-
static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
452-
{
453-
zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
454-
return NULL;
455-
}
456-
/* }}} */
457-
458452
/* int return due to Object Handler API */
459453
static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
460454
{
@@ -736,7 +730,6 @@ void zend_register_closure_ce(void) /* {{{ */
736730

737731
memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers));
738732
closure_handlers.free_obj = zend_closure_free_storage;
739-
closure_handlers.get_constructor = zend_closure_get_constructor;
740733
closure_handlers.get_method = zend_closure_get_method;
741734
closure_handlers.compare = zend_closure_compare;
742735
closure_handlers.clone_obj = zend_closure_clone;

Zend/zend_closures.stub.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* @strict-properties
77
* @not-serializable
88
*/
9+
#[\NonInstantiableClass("Instantiation of class Closure is not allowed")]
910
final class Closure
1011
{
1112
private function __construct() {}

0 commit comments

Comments
 (0)