Skip to content

Commit c58d193

Browse files
committed
Make partial init opt-in with flag rather than return true
1 parent c8ce478 commit c58d193

File tree

9 files changed

+39
-18
lines changed

9 files changed

+39
-18
lines changed

Zend/tests/lazy_objects/gh15999_001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Error: Lazy object was released during initialization
105105
string(13) "C::__destruct"
106106
# Nested error (ghost):
107107
Error: Lazy object was released during initialization
108-
TypeError: Lazy object initializer must return NULL, no value or true
108+
TypeError: Lazy object initializer must return NULL or no value
109109
# Nested error (proxy):
110110
Error: Lazy object was released during initialization
111111
TypeError: The real instance class stdClass is not compatible with the proxy class C. The proxy must be a instance of the same class as the real instance, or a sub-class with no additional properties, and no overrides of the __destructor or __clone methods.

Zend/tests/lazy_objects/gh19224.phpt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,21 @@ $initializer = function ($obj, $prop) {
1616
if ($prop === null || $prop === 'y') {
1717
$obj->y = 2;
1818
}
19-
return true;
2019
};
2120

22-
$obj = $rc->newLazyGhost($initializer);
21+
$obj = $rc->newLazyGhost($initializer, ReflectionClass::PARTIAL_INITIALIZATION);
2322
var_dump($obj);
2423
var_dump($obj->x);
2524
var_dump($obj);
2625
var_dump($obj->y);
2726
var_dump($obj);
2827

29-
$obj = $rc->newLazyGhost($initializer);
28+
$obj = $rc->newLazyGhost($initializer, ReflectionClass::PARTIAL_INITIALIZATION);
3029
foreach ($obj as $prop) {}
3130
var_dump($obj);
3231

3332
// Object is realized when no specific prop is requested.
34-
$obj = $rc->newLazyGhost(function () {});
33+
$obj = $rc->newLazyGhost(function () {}, ReflectionClass::PARTIAL_INITIALIZATION);
3534
foreach ($obj as $prop) {}
3635
var_dump($obj);
3736

Zend/tests/lazy_objects/init_exception_001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ for ($i = 0; $i < 2; $i++) {
2424
--EXPECTF--
2525
Fatal error: Uncaught Exception in %s:%d
2626
Stack trace:
27-
#0 %s(%d): {closure:%s:%d}(Object(C), 'c')
27+
#0 %s(%d): {closure:%s:%d}(Object(C))
2828
#1 {main}
2929
thrown in %s on line %d

Zend/tests/lazy_objects/initializer_must_return_the_right_type.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ lazy ghost object(C)#%d (0) {
106106
uninitialized(int)
107107
}
108108
string(11) "initializer"
109-
TypeError: Lazy object initializer must return NULL, no value or true
109+
TypeError: Lazy object initializer must return NULL or no value
110110
lazy ghost object(C)#%d (0) {
111111
["b"]=>
112112
uninitialized(int)

Zend/zend_lazy_objects.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,9 @@ ZEND_API zend_object *zend_lazy_object_init_ex(zend_object *obj, zend_string *pr
595595

596596
zend_fcall_info_cache *initializer = zend_lazy_object_get_initializer_fcc(obj);
597597

598+
zend_lazy_object_info *info = zend_lazy_object_get_info(obj);
599+
bool partial_initialization = info->flags & ZEND_LAZY_OBJECT_PARTIAL_INITIALIZATION;
600+
598601
/* Prevent reentrant initialization */
599602
OBJ_EXTRA_FLAGS(obj) &= ~IS_OBJ_LAZY_UNINITIALIZED;
600603

@@ -623,16 +626,18 @@ ZEND_API zend_object *zend_lazy_object_init_ex(zend_object *obj, zend_string *pr
623626

624627
/* Call initializer */
625628
zval retval;
626-
int argc = 2;
629+
int argc = partial_initialization ? 2 : 1;
627630
HashTable *named_params = NULL;
628631
zend_object *instance = NULL;
629632

630633
zval args[2];
631634
ZVAL_OBJ(&args[0], obj);
632-
if (prop) {
633-
ZVAL_STR(&args[1], prop);
634-
} else {
635-
ZVAL_NULL(&args[1]);
635+
if (partial_initialization) {
636+
if (prop) {
637+
ZVAL_STR(&args[1], prop);
638+
} else {
639+
ZVAL_NULL(&args[1]);
640+
}
636641
}
637642

638643
zend_call_known_fcc(initializer, &retval, argc, args, named_params);
@@ -642,16 +647,16 @@ ZEND_API zend_object *zend_lazy_object_init_ex(zend_object *obj, zend_string *pr
642647
goto exit;
643648
}
644649

645-
if (Z_TYPE(retval) != IS_NULL && Z_TYPE(retval) != IS_TRUE) {
650+
if (Z_TYPE(retval) != IS_NULL) {
646651
zend_lazy_object_revert_init(obj, properties_table_snapshot, properties_snapshot);
647652
zval_ptr_dtor(&retval);
648-
zend_type_error("Lazy object initializer must return NULL, no value or true");
653+
zend_type_error("Lazy object initializer must return NULL or no value");
649654
goto exit;
650655
}
651656

652657
/* Restore IS_PROP_LAZY flags for flags that remain uninitialized. */
653658
int lazy_properties_count = 0;
654-
if (Z_TYPE(retval) == IS_TRUE && prop) {
659+
if (partial_initialization && prop) {
655660
if (ce->default_properties_count) {
656661
for (int i = 0; i < ce->default_properties_count; i++) {
657662
zval *prop = &obj->properties_table[i];
@@ -664,7 +669,6 @@ ZEND_API zend_object *zend_lazy_object_init_ex(zend_object *obj, zend_string *pr
664669
}
665670
if (lazy_properties_count != 0) {
666671
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED;
667-
zend_lazy_object_info *info = zend_lazy_object_get_info(obj);
668672
info->lazy_properties_count = lazy_properties_count;
669673
}
670674
}

Zend/zend_lazy_objects.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,13 @@
3636
/* Do not call destructor when making existing object lazy */
3737
#define ZEND_LAZY_OBJECT_SKIP_DESTRUCTOR (1<<4)
3838

39+
/* Initialization may not initialize all properties, keeping the object lazy. */
40+
#define ZEND_LAZY_OBJECT_PARTIAL_INITIALIZATION (1<<5)
41+
3942
#define ZEND_LAZY_OBJECT_USER_MASK ( \
4043
ZEND_LAZY_OBJECT_SKIP_INITIALIZATION_ON_SERIALIZE | \
41-
ZEND_LAZY_OBJECT_SKIP_DESTRUCTOR \
44+
ZEND_LAZY_OBJECT_SKIP_DESTRUCTOR | \
45+
ZEND_LAZY_OBJECT_PARTIAL_INITIALIZATION \
4246
)
4347

4448
#define ZEND_LAZY_OBJECT_STRATEGY_MASK ( \

ext/reflection/php_reflection.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5148,6 +5148,11 @@ void reflection_class_new_lazy(INTERNAL_FUNCTION_PARAMETERS,
51485148
RETURN_THROWS();
51495149
}
51505150

5151+
if (strategy != ZEND_LAZY_OBJECT_STRATEGY_GHOST && (options & ZEND_LAZY_OBJECT_PARTIAL_INITIALIZATION)) {
5152+
zend_argument_error(reflection_exception_ptr, 2,
5153+
"does not accept ReflectionClass::PARTIAL_INITIALIZATION");
5154+
}
5155+
51515156
if (is_reset) {
51525157
if (zend_object_is_lazy(obj) && !zend_lazy_object_initialized(obj)) {
51535158
zend_throw_exception_ex(reflection_exception_ptr, 0, "Object is already lazy");

ext/reflection/php_reflection.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ class ReflectionClass implements Reflector
250250
/** @cvalue ZEND_LAZY_OBJECT_SKIP_DESTRUCTOR */
251251
public const int SKIP_DESTRUCTOR = UNKNOWN;
252252

253+
/** @cvalue ZEND_LAZY_OBJECT_PARTIAL_INITIALIZATION */
254+
public const int PARTIAL_INITIALIZATION = UNKNOWN;
255+
253256
public string $name;
254257

255258
private function __clone(): void {}

ext/reflection/php_reflection_arginfo.h

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

0 commit comments

Comments
 (0)