mirror of
https://github.com/php/php-src.git
synced 2024-11-23 01:44:06 +08:00
Lazy objects
RFC: https://wiki.php.net/rfc/lazy-objects Closes GH-15019
This commit is contained in:
parent
e12188fe89
commit
58aa6fc830
1
NEWS
1
NEWS
@ -5,6 +5,7 @@ PHP NEWS
|
||||
- Core:
|
||||
. Fixed bug GH-15330 (Do not scan generator frames more than once). (Arnaud)
|
||||
. Fixed bug GH-15644 (Asymmetric visibility doesn't work with hooks). (ilutov)
|
||||
. Implemented lazy objects RFC. (Arnaud)
|
||||
|
||||
- DOM:
|
||||
. Fixed bug GH-13988 (Storing DOMElement consume 4 times more memory in
|
||||
|
44
UPGRADING
44
UPGRADING
@ -164,6 +164,20 @@ PHP 8.4 UPGRADE NOTES
|
||||
. The DSN's credentials, when set, are given priority over their PDO
|
||||
constructor counterparts, being closer to the documentation states.
|
||||
|
||||
- Reflection:
|
||||
. Added methods ReflectionClass::newLazyGhost(),
|
||||
ReflectionClass::newLazyProxy(), ReflectionClass::resetAsLazyGhost(),
|
||||
ReflectionClass::resetAsLazyProxy(),
|
||||
ReflectionClass::isUninitializedLazyObject(),
|
||||
ReflectionClass::initializeLazyObject(),
|
||||
ReflectionClass::markLazyObjectAsInitialized(),
|
||||
ReflectionClass::getLazyInitializer(),
|
||||
ReflectionProperty::skipLazyInitialization(),
|
||||
ReflectionProperty::setRawValueWithoutLazyInitialization() and constants
|
||||
ReflectionClass::SKIP_*.
|
||||
If you have a method or constant with the same name, you might encounter
|
||||
errors if the declaration is incompatible.
|
||||
|
||||
- SimpleXML:
|
||||
. Get methods called, or casting to a string on a SimpleXMLElement will no
|
||||
longer implicitly reset the iterator data, unless explicitly rewound.
|
||||
@ -269,6 +283,8 @@ PHP 8.4 UPGRADE NOTES
|
||||
See Zend/tests/use_function/ns_end_resets_seen_symbols_1.phpt.
|
||||
. Implemented asymmetric property visibility.
|
||||
RFC: https://wiki.php.net/rfc/asymmetric-visibility-v2
|
||||
. Implemented lazy objects.
|
||||
RFC: https://wiki.php.net/rfc/lazy-objects
|
||||
|
||||
- Curl:
|
||||
. curl_version() returns an additional feature_list value, which is an
|
||||
@ -393,6 +409,20 @@ PHP 8.4 UPGRADE NOTES
|
||||
. ReflectionConstant was introduced.
|
||||
. ReflectionClassConstant::isDeprecated() was introduced.
|
||||
. ReflectionGenerator::isClosed() was introduced.
|
||||
. Multiple methods and constants related to lazy objects were introduced:
|
||||
- ReflectionClass::newLazyGhost()
|
||||
- ReflectionClass::newLazyProxy()
|
||||
- ReflectionClass::resetAsLazyGhost()
|
||||
- ReflectionClass::resetAsLazyProxy()
|
||||
- ReflectionClass::isUninitializedLazyObject()
|
||||
- ReflectionClass::initializeLazyObject()
|
||||
- ReflectionClass::markLazyObjectAsInitialized()
|
||||
- ReflectionClass::getLazyInitializer()
|
||||
- ReflectionProperty::skipLazyInitialization()
|
||||
- ReflectionProperty::setRawValueWithoutLazyInitialization()
|
||||
- ReflectionClass::SKIP_INITIALIZATION_ON_SERIALIZE
|
||||
- ReflectionClass::SKIP_DESTRUCTOR
|
||||
RFC: https://wiki.php.net/rfc/lazy-objects
|
||||
|
||||
- Standard:
|
||||
. stream_bucket_make_writeable() and stream_bucket_new() will now return a
|
||||
@ -780,6 +810,20 @@ PHP 8.4 UPGRADE NOTES
|
||||
. Added pg_set_chunked_rows_size to allow to fetch results in chunk of
|
||||
max N rows.
|
||||
|
||||
- Reflection:
|
||||
. Multiple methods related to lazy objects were introduced:
|
||||
- ReflectionClass::newLazyGhost()
|
||||
- ReflectionClass::newLazyProxy()
|
||||
- ReflectionClass::resetAsLazyGhost()
|
||||
- ReflectionClass::resetAsLazyProxy()
|
||||
- ReflectionClass::isUninitializedLazyObject()
|
||||
- ReflectionClass::initializeLazyObject()
|
||||
- ReflectionClass::markLazyObjectAsInitialized()
|
||||
- ReflectionClass::getLazyInitializer()
|
||||
- ReflectionProperty::skipLazyInitialization()
|
||||
- ReflectionProperty::setRawValueWithoutLazyInitialization()
|
||||
RFC: https://wiki.php.net/rfc/lazy-objects
|
||||
|
||||
- Sodium:
|
||||
. Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
|
||||
functions to support the AEGIS family of authenticated encryption algorithms,
|
||||
|
@ -99,6 +99,9 @@ PHP 8.4 INTERNALS UPGRADE NOTES
|
||||
zend_std_get_properties(). Use zend_std_get_properties_ex() or
|
||||
zend_std_get_properties() instead.
|
||||
|
||||
* zend_object.properties must not be accessed directly. Use
|
||||
zend_std_get_properties_ex() instead.
|
||||
|
||||
* Removed IS_STATIC_VAR_UNINITIALIZED constant. Check for IS_NULL in the
|
||||
static_variables array instead.
|
||||
|
||||
|
77
Zend/tests/lazy_objects/clone_calls___clone_once.phpt
Normal file
77
Zend/tests/lazy_objects/clone_calls___clone_once.phpt
Normal file
@ -0,0 +1,77 @@
|
||||
--TEST--
|
||||
Lazy objects: clone calls __clone() once
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function __clone() {
|
||||
var_dump("clone");
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj::class);
|
||||
$clone = clone $obj;
|
||||
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
var_dump($reflector->isUninitializedLazyObject($clone));
|
||||
var_dump($clone);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(5) "clone"
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(5) "clone"
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
Lazy objects: clone is independant of the original object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class SomeObj {
|
||||
public string $foo = 'A';
|
||||
public string $dummy;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(SomeObj::class);
|
||||
|
||||
$predefinedObject = new SomeObj();
|
||||
$initializer = function () use ($predefinedObject) {
|
||||
return $predefinedObject;
|
||||
};
|
||||
|
||||
$myProxy = $reflector->newLazyProxy($initializer);
|
||||
$reflector->getProperty('foo')->skipLazyInitialization($myProxy);
|
||||
|
||||
$clonedProxy = clone $myProxy;
|
||||
var_dump($clonedProxy->foo);
|
||||
|
||||
$reflector->initializeLazyObject($myProxy);
|
||||
$myProxy->foo = 'B';
|
||||
|
||||
$reflector->initializeLazyObject($clonedProxy);
|
||||
|
||||
var_dump($myProxy->foo);
|
||||
var_dump($clonedProxy->foo);
|
||||
|
||||
--EXPECT--
|
||||
string(1) "A"
|
||||
string(1) "B"
|
||||
string(1) "A"
|
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
Lazy objects: clone is independant of the original object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class SomeObj {
|
||||
public Value $value;
|
||||
public function __construct() {
|
||||
$this->value = new Value();
|
||||
}
|
||||
public function __clone() {
|
||||
$this->value = clone $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
class Value {
|
||||
public string $value = 'A';
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(SomeObj::class);
|
||||
|
||||
$clonedObj = clone $obj;
|
||||
var_dump($clonedObj->value->value);
|
||||
|
||||
$reflector->initializeLazyObject($obj);
|
||||
$obj->value->value = 'B';
|
||||
|
||||
$reflector->initializeLazyObject($clonedObj);
|
||||
|
||||
var_dump($obj->value->value);
|
||||
var_dump($clonedObj->value->value);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(SomeObj::class);
|
||||
|
||||
test('Ghost', $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->__construct();
|
||||
}));
|
||||
|
||||
test('Proxy', $reflector->newLazyProxy(function () {
|
||||
return new SomeObj();
|
||||
}));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(1) "A"
|
||||
string(1) "B"
|
||||
string(1) "A"
|
||||
# Proxy:
|
||||
string(1) "A"
|
||||
string(1) "B"
|
||||
string(1) "A"
|
@ -0,0 +1,40 @@
|
||||
--TEST--
|
||||
Lazy objects: clone is independant of the original object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class SomeObj {
|
||||
public string $foo = 'X';
|
||||
public string $dummy;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(SomeObj::class);
|
||||
|
||||
$clonedObj = clone $obj;
|
||||
|
||||
$reflector->initializeLazyObject($obj);
|
||||
$reflector->getProperty('foo')->setRawValueWithoutLazyInitialization($clonedObj, 'Y');
|
||||
|
||||
$reflector->initializeLazyObject($clonedObj);
|
||||
|
||||
var_dump($clonedObj->foo);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(SomeObj::class);
|
||||
|
||||
test('Ghost', $reflector->newLazyGhost(function ($obj) {
|
||||
}));
|
||||
|
||||
test('Proxy', $reflector->newLazyProxy(function () {
|
||||
return new SomeObj();
|
||||
}));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(1) "Y"
|
||||
# Proxy:
|
||||
string(1) "Y"
|
73
Zend/tests/lazy_objects/clone_initialized.phpt
Normal file
73
Zend/tests/lazy_objects/clone_initialized.phpt
Normal file
@ -0,0 +1,73 @@
|
||||
--TEST--
|
||||
Lazy objects: clone of initialized lazy object does not initialize twice
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj::class);
|
||||
$reflector->initializeLazyObject($obj);
|
||||
|
||||
$clone = clone $obj;
|
||||
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
var_dump($reflector->isUninitializedLazyObject($clone));
|
||||
var_dump($clone);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
56
Zend/tests/lazy_objects/clone_initializer_exception.phpt
Normal file
56
Zend/tests/lazy_objects/clone_initializer_exception.phpt
Normal file
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
Lazy objects: clone: initializer exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj::class);
|
||||
|
||||
try {
|
||||
$clone = clone $obj;
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new \Exception('initializer');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new \Exception('initializer');
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
Exception: initializer
|
||||
bool(true)
|
||||
lazy ghost object(C)#%d (0) {
|
||||
}
|
||||
# Proxy:
|
||||
Exception: initializer
|
||||
bool(true)
|
||||
lazy proxy object(C)#%d (0) {
|
||||
}
|
71
Zend/tests/lazy_objects/clone_initializes.phpt
Normal file
71
Zend/tests/lazy_objects/clone_initializes.phpt
Normal file
@ -0,0 +1,71 @@
|
||||
--TEST--
|
||||
Lazy objects: clone initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj::class);
|
||||
$clone = clone $obj;
|
||||
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
var_dump($reflector->isUninitializedLazyObject($clone));
|
||||
var_dump($clone);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(false)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
bool(false)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
36
Zend/tests/lazy_objects/clone_preverves_object_class.phpt
Normal file
36
Zend/tests/lazy_objects/clone_preverves_object_class.phpt
Normal file
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
Lazy objects: clone returns an object of the same class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public function __construct(
|
||||
public string $property,
|
||||
) {}
|
||||
}
|
||||
class B extends A {
|
||||
public function foo() { }
|
||||
}
|
||||
|
||||
function only_b(B $b) { $b->foo(); }
|
||||
|
||||
$r = new ReflectionClass(B::class);
|
||||
$b = $r->newLazyProxy(function ($obj) {
|
||||
return new A('value');
|
||||
});
|
||||
|
||||
$b->property = 'init_please';
|
||||
|
||||
$clone = clone $b;
|
||||
only_b($b);
|
||||
only_b($clone);
|
||||
|
||||
var_dump($b::class);
|
||||
var_dump($clone::class);
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
string(1) "B"
|
||||
string(1) "B"
|
||||
==DONE==
|
93
Zend/tests/lazy_objects/convert_to_array.phpt
Normal file
93
Zend/tests/lazy_objects/convert_to_array.phpt
Normal file
@ -0,0 +1,93 @@
|
||||
--TEST--
|
||||
Lazy objects: Convertion to array
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public $b;
|
||||
|
||||
public function __construct() {
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 3);
|
||||
|
||||
$a = [];
|
||||
// Converts $obj to array internally
|
||||
array_splice($a, 0, 0, $obj);
|
||||
var_dump($a, $obj);
|
||||
|
||||
$reflector->initializeLazyObject($obj);
|
||||
|
||||
$a = [];
|
||||
array_splice($a, 0, 0, $obj);
|
||||
var_dump($a, $obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function () {
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
# Ghost
|
||||
array(1) {
|
||||
[0]=>
|
||||
int(3)
|
||||
}
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
}
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy
|
||||
array(1) {
|
||||
[0]=>
|
||||
int(3)
|
||||
}
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
}
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
66
Zend/tests/lazy_objects/dtor_called_if_init.phpt
Normal file
66
Zend/tests/lazy_objects/dtor_called_if_init.phpt
Normal file
@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
Lazy objects: destructor of initialized objets is called
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
|
||||
public function __destruct() {
|
||||
var_dump(__METHOD__, $this);
|
||||
}
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
print "In makeLazy\n";
|
||||
$obj = $reflector->newLazyGhost(function () {
|
||||
var_dump("initializer");
|
||||
});
|
||||
print "After makeLazy\n";
|
||||
|
||||
var_dump($obj->a);
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
print "In makeLazy\n";
|
||||
$obj = $reflector->newLazyProxy(function () {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
print "After makeLazy\n";
|
||||
|
||||
var_dump($obj->a);
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
In makeLazy
|
||||
After makeLazy
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
string(13) "C::__destruct"
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
In makeLazy
|
||||
After makeLazy
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
string(13) "C::__destruct"
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
43
Zend/tests/lazy_objects/dtor_not_called_if_not_init.phpt
Normal file
43
Zend/tests/lazy_objects/dtor_not_called_if_not_init.phpt
Normal file
@ -0,0 +1,43 @@
|
||||
--TEST--
|
||||
Lazy objects: destructor of lazy objets is not called if not initialized
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public function __destruct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
print "In newLazyGhost\n";
|
||||
$obj = $reflector->newLazyGhost(function () {
|
||||
var_dump("initializer");
|
||||
});
|
||||
print "After newLazyGhost\n";
|
||||
|
||||
// Does not call destructor
|
||||
$obj = null;
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
print "In newLazyProxy\n";
|
||||
$obj = $reflector->newLazyProxy(function () {
|
||||
var_dump("initializer");
|
||||
});
|
||||
print "After newLazyGhost\n";
|
||||
|
||||
// Does not call destructor
|
||||
$obj = null;
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
In newLazyGhost
|
||||
After newLazyGhost
|
||||
# Proxy:
|
||||
In newLazyProxy
|
||||
After newLazyGhost
|
63
Zend/tests/lazy_objects/fetch_coalesce_initializes.phpt
Normal file
63
Zend/tests/lazy_objects/fetch_coalesce_initializes.phpt
Normal file
@ -0,0 +1,63 @@
|
||||
--TEST--
|
||||
Lazy objects: property fetch coalesce initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a ?? null);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
--TEST--
|
||||
Lazy objects: property fetch coalesce on non existing property initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->unknown ?? null);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
72
Zend/tests/lazy_objects/fetch_declared_prop_initializes.phpt
Normal file
72
Zend/tests/lazy_objects/fetch_declared_prop_initializes.phpt
Normal file
@ -0,0 +1,72 @@
|
||||
--TEST--
|
||||
Lazy objects: property fetch initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
65
Zend/tests/lazy_objects/fetch_dynamic_prop_initializes.phpt
Normal file
65
Zend/tests/lazy_objects/fetch_dynamic_prop_initializes.phpt
Normal file
@ -0,0 +1,65 @@
|
||||
--TEST--
|
||||
Lazy objects: property fetch of dynamic property initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump(@$obj->dynamic);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
}
|
75
Zend/tests/lazy_objects/fetch_hook_may_not_initialize.phpt
Normal file
75
Zend/tests/lazy_objects/fetch_hook_may_not_initialize.phpt
Normal file
@ -0,0 +1,75 @@
|
||||
--TEST--
|
||||
Lazy objects: hooked property fetch does not initialize object if hook does not observe object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a {
|
||||
get { return $this->a; }
|
||||
set($value) { $this->a = $value; }
|
||||
}
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
--TEST--
|
||||
Lazy objects: virtual hooked property fetch may initialize object if hook observes object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $_a;
|
||||
public $a {
|
||||
&get { return $this->_a; }
|
||||
}
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->_a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$a = &$obj->a;
|
||||
var_dump($a);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (2) {
|
||||
["_a"]=>
|
||||
&int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["_a"]=>
|
||||
&int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
Lazy objects: virtual hooked property fetch does not initialize object if hook does not observe object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a {
|
||||
get { return 1; }
|
||||
}
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
int(1)
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
66
Zend/tests/lazy_objects/fetch_magic_prop_may_initialize.phpt
Normal file
66
Zend/tests/lazy_objects/fetch_magic_prop_may_initialize.phpt
Normal file
@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
Lazy objects: magic property fetch initializes object if magic method observes object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
public function __get($name) {
|
||||
return $this->a;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->magic);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Lazy objects: magic property fetch does not not initialize object if magic method does not observe object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
public function __get($name) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->magic);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(5) "magic"
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(5) "magic"
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
--TEST--
|
||||
Lazy objects: recursive magic property fetch initializes object if magic method observes object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
public function __get($name) {
|
||||
return $this->$name;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->magic);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
|
||||
Warning: Undefined property: C::$magic in %s on line %d
|
||||
NULL
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
|
||||
Warning: Undefined property: C::$magic in %s on line %d
|
||||
NULL
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
62
Zend/tests/lazy_objects/fetch_op_dynamic_error.phpt
Normal file
62
Zend/tests/lazy_objects/fetch_op_dynamic_error.phpt
Normal file
@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
Lazy objects: dynamic property op error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
try {
|
||||
var_dump(@$obj->dynamic++);
|
||||
} catch(Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new Error("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new Error("initializer");
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
Error: initializer
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
Error: initializer
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
Lazy objects: dynamic property op initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump(@$obj->dynamic++);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["dynamic"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["dynamic"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
62
Zend/tests/lazy_objects/fetch_op_error.phpt
Normal file
62
Zend/tests/lazy_objects/fetch_op_error.phpt
Normal file
@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
Lazy objects: property op error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
|
||||
try {
|
||||
var_dump($obj->a++);
|
||||
} catch (Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new Error("initializer");
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new Error("initializer");
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
Error: initializer
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
Error: initializer
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
65
Zend/tests/lazy_objects/fetch_op_initializes.phpt
Normal file
65
Zend/tests/lazy_objects/fetch_op_initializes.phpt
Normal file
@ -0,0 +1,65 @@
|
||||
--TEST--
|
||||
Lazy objects: property op initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a++);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(2)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(2)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%s (1) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
--TEST--
|
||||
Lazy objects: property op on skipped property does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
public int $c;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj);
|
||||
$reflector->getProperty('a')->skipLazyInitialization($obj);
|
||||
$reflector->getProperty('b')->skipLazyInitialization($obj);
|
||||
$reflector->getProperty('c')->skipLazyInitialization($obj);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a++);
|
||||
var_dump($obj->b++);
|
||||
try {
|
||||
var_dump($obj->c++);
|
||||
} catch (Error $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new c();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
NULL
|
||||
int(1)
|
||||
Typed property C::$c must not be accessed before initialization
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
NULL
|
||||
int(1)
|
||||
Typed property C::$c must not be accessed before initialization
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
65
Zend/tests/lazy_objects/fetch_ref_initializes.phpt
Normal file
65
Zend/tests/lazy_objects/fetch_ref_initializes.phpt
Normal file
@ -0,0 +1,65 @@
|
||||
--TEST--
|
||||
Lazy objects: property fetch ref initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$ref = &$obj->a;
|
||||
var_dump($ref);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
&int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
&int(1)
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
--TEST--
|
||||
Lazy objects: fetch ref on skipped property does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
public int $c;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$ref = &$obj->a;
|
||||
$ref = &$obj->b;
|
||||
try {
|
||||
$ref = &$obj->c;
|
||||
} catch (Error $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
var_dump($ref);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
Cannot access uninitialized non-nullable property C::$c by reference
|
||||
int(1)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
&int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
Cannot access uninitialized non-nullable property C::$c by reference
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
&int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
--TEST--
|
||||
Lazy objects: fetch skipped property does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
public int $c;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass($obj);
|
||||
$reflector->getProperty('a')->skipLazyInitialization($obj);
|
||||
$reflector->getProperty('b')->skipLazyInitialization($obj);
|
||||
$reflector->getProperty('c')->skipLazyInitialization($obj);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj->b);
|
||||
try {
|
||||
var_dump($obj->c);
|
||||
} catch (Error $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
NULL
|
||||
int(1)
|
||||
Typed property C::$c must not be accessed before initialization
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
NULL
|
||||
int(1)
|
||||
Typed property C::$c must not be accessed before initialization
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
int(1)
|
||||
["c"]=>
|
||||
uninitialized(int)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
--TEST--
|
||||
Lazy objects: final classes can be initialized lazily
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
final class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
54
Zend/tests/lazy_objects/gc_001.phpt
Normal file
54
Zend/tests/lazy_objects/gc_001.phpt
Normal file
@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 001
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Canary {
|
||||
public function __destruct() {
|
||||
var_dump(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
printf("# Ghost:\n");
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyGhost($obj, function () use ($canary) {
|
||||
});
|
||||
|
||||
$canary = null;
|
||||
$obj = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
printf("# Proxy:\n");
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyProxy($obj, function () use ($canary) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
$canary = null;
|
||||
$obj = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(10) "__destruct"
|
||||
# Proxy:
|
||||
string(10) "__destruct"
|
||||
==DONE==
|
59
Zend/tests/lazy_objects/gc_002.phpt
Normal file
59
Zend/tests/lazy_objects/gc_002.phpt
Normal file
@ -0,0 +1,59 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 002
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Canary {
|
||||
public $value;
|
||||
public function __destruct() {
|
||||
var_dump(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
printf("# Ghost:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyGhost($obj, function () use ($canary) {
|
||||
});
|
||||
|
||||
$canary->value = $obj;
|
||||
$canary = null;
|
||||
$obj = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
printf("# Proxy:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyProxy($obj, function () use ($canary) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
$canary->value = $obj;
|
||||
$canary = null;
|
||||
$obj = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(10) "__destruct"
|
||||
# Proxy:
|
||||
string(10) "__destruct"
|
||||
==DONE==
|
59
Zend/tests/lazy_objects/gc_003.phpt
Normal file
59
Zend/tests/lazy_objects/gc_003.phpt
Normal file
@ -0,0 +1,59 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 003
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Canary {
|
||||
public $value;
|
||||
public function __destruct() {
|
||||
var_dump(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
printf("# Ghost:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyGhost($obj, function () use ($canary) {
|
||||
});
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
printf("# Proxy:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyProxy($obj, function () use ($canary) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(10) "__destruct"
|
||||
# Proxy:
|
||||
string(10) "__destruct"
|
||||
==DONE==
|
67
Zend/tests/lazy_objects/gc_004.phpt
Normal file
67
Zend/tests/lazy_objects/gc_004.phpt
Normal file
@ -0,0 +1,67 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 004
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Canary {
|
||||
public $value;
|
||||
public function __destruct() {
|
||||
var_dump(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
printf("# Ghost:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyGhost($obj, function () use ($canary) {
|
||||
});
|
||||
|
||||
var_dump($obj); // initializes property hash
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
printf("# Proxy:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$obj = (new ReflectionClass(C::class))->newInstanceWithoutConstructor();
|
||||
(new ReflectionClass($obj))->resetAsLazyProxy($obj, function () use ($canary) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump($obj); // initializes property hash
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
object(C)#%d (0) {
|
||||
}
|
||||
string(10) "__destruct"
|
||||
# Proxy:
|
||||
object(C)#%d (0) {
|
||||
}
|
||||
string(10) "__destruct"
|
||||
==DONE==
|
68
Zend/tests/lazy_objects/gc_005.phpt
Normal file
68
Zend/tests/lazy_objects/gc_005.phpt
Normal file
@ -0,0 +1,68 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 005
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Canary {
|
||||
public $value;
|
||||
public function __destruct() {
|
||||
var_dump(__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
public $value;
|
||||
}
|
||||
|
||||
function ghost() {
|
||||
printf("# Ghost:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$obj = $reflector->newInstanceWithoutConstructor();
|
||||
$reflector->resetAsLazyGhost($obj, function () use ($canary) {
|
||||
});
|
||||
|
||||
$reflector->getProperty('value')->setRawValueWithoutLazyInitialization($obj, $obj);
|
||||
$reflector = null;
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
function proxy() {
|
||||
printf("# Proxy:\n");
|
||||
|
||||
$canary = new Canary();
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$obj = $reflector->newInstanceWithoutConstructor();
|
||||
$reflector->resetAsLazyProxy($obj, function () use ($canary) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
$reflector->getProperty('value')->setRawValueWithoutLazyInitialization($obj, $obj);
|
||||
$reflector = null;
|
||||
|
||||
$canary->value = $obj;
|
||||
$obj = null;
|
||||
$canary = null;
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
ghost();
|
||||
proxy();
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(10) "__destruct"
|
||||
# Proxy:
|
||||
string(10) "__destruct"
|
||||
==DONE==
|
36
Zend/tests/lazy_objects/gc_006.phpt
Normal file
36
Zend/tests/lazy_objects/gc_006.phpt
Normal file
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
Lazy objects: GC 006
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
public $foo;
|
||||
}
|
||||
|
||||
class Initializer {
|
||||
public function __invoke($obj) {
|
||||
$obj->foo = $this;
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
public function __destruct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(Foo::class);
|
||||
$foo = $reflector->newLazyGhost(new Initializer());
|
||||
|
||||
print "Dump\n";
|
||||
|
||||
var_dump($foo->foo);
|
||||
|
||||
print "Done\n";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Dump
|
||||
string(21) "Initializer::__invoke"
|
||||
object(Initializer)#%d (0) {
|
||||
}
|
||||
Done
|
||||
string(23) "Initializer::__destruct"
|
52
Zend/tests/lazy_objects/get_initializer.phpt
Normal file
52
Zend/tests/lazy_objects/get_initializer.phpt
Normal file
@ -0,0 +1,52 @@
|
||||
--TEST--
|
||||
Lazy objects: getLazyInitializer() returns initializer
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
|
||||
public static function initStatic() {}
|
||||
public function init() {}
|
||||
}
|
||||
|
||||
function foo() {
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$initializers = [
|
||||
'foo',
|
||||
foo(...),
|
||||
function () {},
|
||||
[C::class, 'initStatic'],
|
||||
[new C(), 'init'],
|
||||
C::initStatic(...),
|
||||
(new C())->init(...),
|
||||
];
|
||||
|
||||
foreach ($initializers as $i => $initializer) {
|
||||
$c = $reflector->newLazyGhost($initializer);
|
||||
if ($reflector->getLazyInitializer($c) !== $initializer) {
|
||||
printf("Initializer %d: failed\n", $i);
|
||||
continue;
|
||||
}
|
||||
|
||||
$reflector->initializeLazyObject($c);
|
||||
if ($reflector->getLazyInitializer($c) !== null) {
|
||||
printf("Initializer %d: failed\n", $i);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Initializer %d: ok\n", $i);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Initializer 0: ok
|
||||
Initializer 1: ok
|
||||
Initializer 2: ok
|
||||
Initializer 3: ok
|
||||
Initializer 4: ok
|
||||
Initializer 5: ok
|
||||
Initializer 6: ok
|
27
Zend/tests/lazy_objects/get_properties.phpt
Normal file
27
Zend/tests/lazy_objects/get_properties.phpt
Normal file
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
Lazy objects: get_properties failure
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public $b;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$a = $reflector->newLazyProxy(function () {
|
||||
throw new \Exception('Initializer');
|
||||
});
|
||||
|
||||
$b = new C();
|
||||
|
||||
try {
|
||||
var_dump($a > $b);
|
||||
} catch (Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Exception: Initializer
|
27
Zend/tests/lazy_objects/init_ast_const.phpt
Normal file
27
Zend/tests/lazy_objects/init_ast_const.phpt
Normal file
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
Lazy objects: Class constants are updated before initialization
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public stdClass $a = FOO;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$c = $reflector->newLazyGhost(function () { });
|
||||
|
||||
function f() {
|
||||
define('FOO', new stdClass);
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
try {
|
||||
var_dump($c->a);
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
object(stdClass)#%d (0) {
|
||||
}
|
26
Zend/tests/lazy_objects/init_ast_const_failure.phpt
Normal file
26
Zend/tests/lazy_objects/init_ast_const_failure.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Lazy objects: Class constants are updated before initialization: update constant failure
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public C $a = FOO;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$c = $reflector->newLazyGhost(function () { });
|
||||
|
||||
function f() {
|
||||
define('FOO', new stdClass);
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
try {
|
||||
var_dump($c->a);
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
--EXPECT--
|
||||
TypeError: Cannot assign stdClass to property C::$a of type C
|
29
Zend/tests/lazy_objects/init_exception_001.phpt
Normal file
29
Zend/tests/lazy_objects/init_exception_001.phpt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
Lazy objects: init exception 001
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a;
|
||||
public $b;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) use ($i) {
|
||||
if ($i === 1) {
|
||||
throw new \Exception();
|
||||
}
|
||||
});
|
||||
$obj->c = 1;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): {closure:%s:%d}(Object(C))
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
@ -0,0 +1,53 @@
|
||||
--TEST--
|
||||
Lazy objects: Object is still lazy after initializer exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
Is lazy: 1
|
@ -0,0 +1,74 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
public int $c;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
(new ReflectionProperty(C::class, 'c'))->setRawValueWithoutLazyInitialization($obj, 0);
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
// Initializer effects on the proxy are not reverted
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy proxy object(C)#%d (3) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
["c"]=>
|
||||
int(5)
|
||||
}
|
||||
Is lazy: 1
|
@ -0,0 +1,79 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception (dynamic properties)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
public int $c;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
(new ReflectionProperty(C::class, 'c'))->setRawValueWithoutLazyInitialization($obj, 0);
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$obj->d = 6;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$obj->d = 6;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
// Initializer effects on the proxy are not reverted
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy proxy object(C)#%d (4) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
["c"]=>
|
||||
int(5)
|
||||
["d"]=>
|
||||
int(6)
|
||||
}
|
||||
Is lazy: 1
|
@ -0,0 +1,86 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception (dynamic properties, initialized hashtable)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
public int $c;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
// Builds properties hashtable
|
||||
var_dump(get_mangled_object_vars($obj));
|
||||
|
||||
(new ReflectionProperty(C::class, 'c'))->setRawValueWithoutLazyInitialization($obj, 0);
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$obj->d = 6;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$obj->d = 6;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
// Initializer effects on the proxy are not reverted
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy proxy object(C)#%d (4) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
["c"]=>
|
||||
int(5)
|
||||
["d"]=>
|
||||
int(6)
|
||||
}
|
||||
Is lazy: 1
|
@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception (nested)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public $a;
|
||||
}
|
||||
class B {
|
||||
public $b;
|
||||
}
|
||||
class C {
|
||||
public $c;
|
||||
}
|
||||
|
||||
$aReflector = new ReflectionClass(A::class);
|
||||
$bReflector = new ReflectionClass(B::class);
|
||||
$cReflector = new ReflectionClass(C::class);
|
||||
|
||||
$c = $cReflector->newLazyGhost(function ($c) {
|
||||
$c->c = 1;
|
||||
});
|
||||
|
||||
$b = $bReflector->newLazyGhost(function () {
|
||||
throw new \Exception('xxx');
|
||||
});
|
||||
|
||||
$a = $aReflector->newLazyGhost(function ($a) use ($b, $c) {
|
||||
$a->a = $c->c + $b->b;
|
||||
});
|
||||
|
||||
try {
|
||||
$a->init = 'please';
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($a, $b, $c);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Exception: xxx
|
||||
lazy ghost object(A)#%d (0) {
|
||||
}
|
||||
lazy ghost object(B)#%d (0) {
|
||||
}
|
||||
object(C)#%d (1) {
|
||||
["c"]=>
|
||||
int(1)
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Lazy objects: Object is still lazy after initializer exception (overridden prop)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class B {
|
||||
public int $b = 1;
|
||||
}
|
||||
class C extends B {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector->getProperty('b')->skipLazyInitialization($obj);
|
||||
$i = 5;
|
||||
$obj->b = &$i;
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
Is lazy: 1
|
@ -0,0 +1,85 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception (properties hashtable)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
public int $c;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
(new ReflectionProperty(C::class, 'c'))->setRawValueWithoutLazyInitialization($obj, 0);
|
||||
|
||||
// Builds properties hashtable
|
||||
var_dump(get_mangled_object_vars($obj));
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
// Initializer effects on the proxy are not reverted
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
array(1) {
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
Is lazy: 1
|
||||
# Proxy:
|
||||
array(1) {
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy proxy object(C)#%d (3) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
["c"]=>
|
||||
int(5)
|
||||
}
|
||||
Is lazy: 1
|
@ -0,0 +1,97 @@
|
||||
--TEST--
|
||||
Lazy objects: Initializer effects are reverted after exception (properties hashtable referenced after initializer)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
public int $c;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
(new ReflectionProperty(C::class, 'c'))->setRawValueWithoutLazyInitialization($obj, 0);
|
||||
|
||||
// Builds properties hashtable
|
||||
var_dump(get_mangled_object_vars($obj));
|
||||
|
||||
try {
|
||||
$reflector->initializeLazyObject($obj);
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump($obj);
|
||||
printf("Is lazy: %d\n", $reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($table);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
global $table;
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$table = (array) $obj;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
global $table;
|
||||
var_dump("initializer");
|
||||
$obj->a = 3;
|
||||
$obj->b = 4;
|
||||
$obj->c = 5;
|
||||
$table = (array) $obj;
|
||||
throw new Exception('initializer exception');
|
||||
});
|
||||
|
||||
// Initializer effects on the proxy are not reverted
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
array(1) {
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
Is lazy: 1
|
||||
|
||||
Warning: Undefined variable $table in %s on line %d
|
||||
NULL
|
||||
# Proxy:
|
||||
array(1) {
|
||||
["c"]=>
|
||||
int(0)
|
||||
}
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
lazy proxy object(C)#%d (3) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
["c"]=>
|
||||
int(5)
|
||||
}
|
||||
Is lazy: 1
|
||||
|
||||
Warning: Undefined variable $table in %s on line %d
|
||||
NULL
|
35
Zend/tests/lazy_objects/init_fatal.phpt
Normal file
35
Zend/tests/lazy_objects/init_fatal.phpt
Normal file
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
Lazy objects: fatal error during initialization of ghost object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public $b;
|
||||
public $c;
|
||||
public function __construct() {
|
||||
eval('{');
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
$reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, new stdClass);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->c);
|
||||
var_dump($obj);
|
||||
|
||||
--EXPECTF--
|
||||
lazy ghost object(C)#%d (1) {
|
||||
["b"]=>
|
||||
object(stdClass)#%d (0) {
|
||||
}
|
||||
}
|
||||
string(11) "initializer"
|
||||
|
||||
Parse error: Unclosed '{' in %s on line %d
|
100
Zend/tests/lazy_objects/init_handles_ref_source_types.phpt
Normal file
100
Zend/tests/lazy_objects/init_handles_ref_source_types.phpt
Normal file
@ -0,0 +1,100 @@
|
||||
--TEST--
|
||||
Lazy objects: Pre-initialization reference source types are properly handled (no initialization exception)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public ?C $a;
|
||||
public ?C $b;
|
||||
public $c;
|
||||
public function __construct() {
|
||||
$this->a = null;
|
||||
unset($this->b);
|
||||
$this->b = null;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, null);
|
||||
$refA = &$obj->a;
|
||||
$reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, null);
|
||||
$refB = &$obj->b;
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->c);
|
||||
var_dump($obj);
|
||||
|
||||
try {
|
||||
// $refA retained its reference source type (except for the proxy
|
||||
// case: its the responsibility of the initializer to propagate
|
||||
// pre-initialized properties to the instance)
|
||||
$refA = 1;
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
// source type was not duplicated
|
||||
unset($obj->a);
|
||||
$refA = 1;
|
||||
|
||||
$refB = 1;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(null);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
string(11) "initializer"
|
||||
NULL
|
||||
object(C)#%d (3) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
NULL
|
||||
["c"]=>
|
||||
NULL
|
||||
}
|
||||
TypeError: Cannot assign int to reference held by property C::$a of type ?C
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
string(11) "initializer"
|
||||
NULL
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (3) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
NULL
|
||||
["c"]=>
|
||||
NULL
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
--TEST--
|
||||
Lazy objects: Pre-initialization reference source types are properly handled after initializer exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public ?C $a;
|
||||
public ?C $b;
|
||||
public $c;
|
||||
public function __construct() {
|
||||
unset($this->b);
|
||||
throw new \Exception('initializer exception');
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, null);
|
||||
$refA = &$obj->a;
|
||||
$reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, null);
|
||||
$refB = &$obj->b;
|
||||
|
||||
var_dump($obj);
|
||||
try {
|
||||
var_dump($obj->c);
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
var_dump($obj);
|
||||
|
||||
try {
|
||||
// $refA retained its reference source type (except for the proxy
|
||||
// case: it is the responsibility of the initializer to propagate
|
||||
// pre-initialized properties to the instance)
|
||||
$refA = 1;
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
// source type was not duplicated
|
||||
unset($obj->a);
|
||||
$refA = 1;
|
||||
|
||||
try {
|
||||
// $refB retained its reference source type
|
||||
$refB = 1;
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
// source type was not duplicated
|
||||
unset($obj->b);
|
||||
$refB = 1;
|
||||
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(null);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
string(11) "initializer"
|
||||
Exception: initializer exception
|
||||
lazy ghost object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
TypeError: Cannot assign int to reference held by property C::$a of type ?C
|
||||
TypeError: Cannot assign int to reference held by property C::$b of type ?C
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
string(11) "initializer"
|
||||
Exception: initializer exception
|
||||
lazy proxy object(C)#%d (2) {
|
||||
["a"]=>
|
||||
&NULL
|
||||
["b"]=>
|
||||
&NULL
|
||||
}
|
||||
TypeError: Cannot assign int to reference held by property C::$a of type ?C
|
||||
TypeError: Cannot assign int to reference held by property C::$b of type ?C
|
46
Zend/tests/lazy_objects/init_may_leave_props_uninit.phpt
Normal file
46
Zend/tests/lazy_objects/init_may_leave_props_uninit.phpt
Normal file
@ -0,0 +1,46 @@
|
||||
--TEST--
|
||||
Lazy objects: properties with no default values are left uninitialized
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
try {
|
||||
var_dump($obj->b);
|
||||
} catch (Error $e) {
|
||||
printf("%s\n", $e);
|
||||
}
|
||||
var_dump($obj);
|
||||
--EXPECTF--
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
NULL
|
||||
Error: Typed property C::$b must not be accessed before initialization in %s:%d
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
NULL
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
29
Zend/tests/lazy_objects/init_preserves_identity.phpt
Normal file
29
Zend/tests/lazy_objects/init_preserves_identity.phpt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
Lazy objects: initialization of proxy does not change object id
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class MyObject {
|
||||
public $a;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(MyObject::class);
|
||||
|
||||
$object = new MyObject();
|
||||
$objectId = spl_object_id($object);
|
||||
|
||||
$reflector->resetAsLazyProxy($object, function (MyObject $object) use (&$object2Id) {
|
||||
$object2 = new MyObject();
|
||||
$object2Id = spl_object_id($object2);
|
||||
return $object2;
|
||||
});
|
||||
var_dump(spl_object_id($object) === $objectId);
|
||||
$reflector->initializeLazyObject($object);
|
||||
var_dump(spl_object_id($object) === $objectId);
|
||||
var_dump(spl_object_id($object) !== $object2Id);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
33
Zend/tests/lazy_objects/init_preserves_proxy_class.phpt
Normal file
33
Zend/tests/lazy_objects/init_preserves_proxy_class.phpt
Normal file
@ -0,0 +1,33 @@
|
||||
--TEST--
|
||||
Lazy objects: initialization of proxy does not change the class of the object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
class A {
|
||||
public string $s;
|
||||
}
|
||||
class B extends A {
|
||||
public function foo() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(B::class);
|
||||
$o = $reflector->newLazyProxy(function (B $o) {
|
||||
return new A();
|
||||
});
|
||||
|
||||
var_dump(get_class($o));
|
||||
$o->foo();
|
||||
$o->s = 'init';
|
||||
var_dump(get_class($o));
|
||||
$o->foo();
|
||||
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(1) "B"
|
||||
string(6) "B::foo"
|
||||
string(1) "B"
|
||||
string(6) "B::foo"
|
47
Zend/tests/lazy_objects/init_sets_prop_default_values.phpt
Normal file
47
Zend/tests/lazy_objects/init_sets_prop_default_values.phpt
Normal file
@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Lazy objects: props are initialized to default values before calling initializer
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
public int $b = 2;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 3;
|
||||
$this->b = 4;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
var_dump($obj);
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
var_dump($obj->a);
|
||||
var_dump($obj);
|
||||
--EXPECTF--
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
string(14) "C::__construct"
|
||||
int(3)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(3)
|
||||
["b"]=>
|
||||
int(4)
|
||||
}
|
58
Zend/tests/lazy_objects/init_trigger_array_cast.phpt
Normal file
58
Zend/tests/lazy_objects/init_trigger_array_cast.phpt
Normal file
@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
Lazy objects: array cast does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump((array)$obj);
|
||||
|
||||
$obj->a = 2;
|
||||
var_dump((array)$obj);
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump((array)$obj);
|
||||
|
||||
$obj->a = 2;
|
||||
var_dump((array)$obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
54
Zend/tests/lazy_objects/init_trigger_compare.phpt
Normal file
54
Zend/tests/lazy_objects/init_trigger_compare.phpt
Normal file
@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
Lazy objects: comparison initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public $b;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return 'C';
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$a = $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
$b = $reflector->newLazyProxy(function ($obj) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump($a > $b);
|
||||
|
||||
$a = $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
$b = $reflector->newLazyProxy(function ($obj) {
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump($a == $b);
|
||||
|
||||
$a = $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump('A' < $a);
|
||||
?>
|
||||
--EXPECT--
|
||||
string(14) "C::__construct"
|
||||
string(14) "C::__construct"
|
||||
bool(false)
|
||||
string(14) "C::__construct"
|
||||
string(14) "C::__construct"
|
||||
bool(true)
|
||||
bool(true)
|
63
Zend/tests/lazy_objects/init_trigger_debug_zval_dump.phpt
Normal file
63
Zend/tests/lazy_objects/init_trigger_debug_zval_dump.phpt
Normal file
@ -0,0 +1,63 @@
|
||||
--TEST--
|
||||
Lazy objects: debug_zval_dump does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
debug_zval_dump($obj);
|
||||
$reflector->initializeLazyObject($obj);
|
||||
debug_zval_dump($obj);
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
debug_zval_dump($obj);
|
||||
$reflector->initializeLazyObject($obj);
|
||||
debug_zval_dump($obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) refcount(2){
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (1) refcount(2){
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) refcount(2){
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) refcount(2){
|
||||
["instance"]=>
|
||||
object(C)#%d (1) refcount(2){
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
48
Zend/tests/lazy_objects/init_trigger_foreach.phpt
Normal file
48
Zend/tests/lazy_objects/init_trigger_foreach.phpt
Normal file
@ -0,0 +1,48 @@
|
||||
--TEST--
|
||||
Lazy objects: Foreach initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
96
Zend/tests/lazy_objects/init_trigger_foreach_hooks.phpt
Normal file
96
Zend/tests/lazy_objects/init_trigger_foreach_hooks.phpt
Normal file
@ -0,0 +1,96 @@
|
||||
--TEST--
|
||||
Lazy objects: Foreach initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a;
|
||||
public int $b {
|
||||
get { return $this->b; }
|
||||
set(int $value) { $this->b = $value; }
|
||||
}
|
||||
public int $c {
|
||||
get { return $this->a + 2; }
|
||||
}
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
$this->d = 4;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Ghost (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new \Exception();
|
||||
});
|
||||
|
||||
try {
|
||||
var_dump(json_encode($obj));
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
print "# Proxy (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new \Exception();
|
||||
});
|
||||
|
||||
try {
|
||||
var_dump(json_encode($obj));
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "c"
|
||||
int(3)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "c"
|
||||
int(3)
|
||||
# Ghost (init exception):
|
||||
Exception:
|
||||
# Proxy (init exception):
|
||||
Exception:
|
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
Lazy objects: get_mangled_object_vars does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump(get_mangled_object_vars($obj));
|
||||
|
||||
$obj->a = 2;
|
||||
var_dump(get_mangled_object_vars($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
array(0) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
62
Zend/tests/lazy_objects/init_trigger_get_object_vars.phpt
Normal file
62
Zend/tests/lazy_objects/init_trigger_get_object_vars.phpt
Normal file
@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
Lazy objects: get_object_vars initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump(get_object_vars($obj));
|
||||
|
||||
$obj->a = 2;
|
||||
var_dump(get_object_vars($obj));
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump(get_object_vars($obj));
|
||||
|
||||
$obj->a = 2;
|
||||
var_dump(get_object_vars($obj));
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
array(1) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
42
Zend/tests/lazy_objects/init_trigger_json_encode.phpt
Normal file
42
Zend/tests/lazy_objects/init_trigger_json_encode.phpt
Normal file
@ -0,0 +1,42 @@
|
||||
--TEST--
|
||||
Lazy objects: json_encode initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump(json_encode($obj));
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump(json_encode($obj));
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(7) "{"a":1}"
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(7) "{"a":1}"
|
80
Zend/tests/lazy_objects/init_trigger_json_encode_hooks.phpt
Normal file
80
Zend/tests/lazy_objects/init_trigger_json_encode_hooks.phpt
Normal file
@ -0,0 +1,80 @@
|
||||
--TEST--
|
||||
Lazy objects: json_encode initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a;
|
||||
public int $b {
|
||||
get { return $this->b; }
|
||||
set(int $value) { $this->b = $value; }
|
||||
}
|
||||
public int $c {
|
||||
get { return $this->a + 2; }
|
||||
}
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
$this->d = 4;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump(json_encode($obj));
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump(json_encode($obj));
|
||||
|
||||
print "# Ghost (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new \Exception();
|
||||
});
|
||||
|
||||
try {
|
||||
var_dump(json_encode($obj));
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
print "# Proxy (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new \Exception();
|
||||
});
|
||||
|
||||
try {
|
||||
var_dump(json_encode($obj));
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(25) "{"a":1,"b":2,"c":3,"d":4}"
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(25) "{"a":1,"b":2,"c":3,"d":4}"
|
||||
# Ghost (init exception):
|
||||
Exception:
|
||||
# Proxy (init exception):
|
||||
Exception:
|
@ -0,0 +1,45 @@
|
||||
--TEST--
|
||||
Lazy objects: ReflectionObject::__toString() does not trigger initialization
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s\n", $name);
|
||||
|
||||
(new ReflectionObject($obj))->__toString();
|
||||
|
||||
printf("Initialized:\n");
|
||||
var_dump(!(new ReflectionClass($obj))->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECT--
|
||||
# Ghost
|
||||
Initialized:
|
||||
bool(false)
|
||||
# Proxy
|
||||
Initialized:
|
||||
bool(false)
|
43
Zend/tests/lazy_objects/init_trigger_serialize.phpt
Normal file
43
Zend/tests/lazy_objects/init_trigger_serialize.phpt
Normal file
@ -0,0 +1,43 @@
|
||||
--TEST--
|
||||
Lazy objects: serialize initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump(serialize($obj));
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump(serialize($obj));
|
||||
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(24) "O:1:"C":1:{s:1:"a";i:1;}"
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(24) "O:1:"C":1:{s:1:"a";i:1;}"
|
63
Zend/tests/lazy_objects/init_trigger_var_dump.phpt
Normal file
63
Zend/tests/lazy_objects/init_trigger_var_dump.phpt
Normal file
@ -0,0 +1,63 @@
|
||||
--TEST--
|
||||
Lazy objects: var_dump does not initialize object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
$reflector->initializeLazyObject($obj);
|
||||
var_dump($obj);
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
$reflector->initializeLazyObject($obj);
|
||||
var_dump($obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["a"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
--TEST--
|
||||
Lazy objects: var_dump may not initialize object with __debugInfo() method
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
public function __debugInfo() {
|
||||
return ['hello'];
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost
|
||||
lazy ghost object(C)#%d (1) {
|
||||
[0]=>
|
||||
string(5) "hello"
|
||||
}
|
||||
Initialized:
|
||||
bool(false)
|
||||
# Proxy
|
||||
lazy proxy object(C)#%d (1) {
|
||||
[0]=>
|
||||
string(5) "hello"
|
||||
}
|
||||
Initialized:
|
||||
bool(false)
|
@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Lazy objects: var_dump may initialize object with __debugInfo() method
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
public function __debugInfo() {
|
||||
return [$this->a];
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
printf("# %s\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (1) {
|
||||
[0]=>
|
||||
int(1)
|
||||
}
|
||||
Initialized:
|
||||
bool(true)
|
||||
# Proxy
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
[0]=>
|
||||
int(1)
|
||||
}
|
||||
Initialized:
|
||||
bool(true)
|
47
Zend/tests/lazy_objects/init_trigger_var_export.phpt
Normal file
47
Zend/tests/lazy_objects/init_trigger_var_export.phpt
Normal file
@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Lazy objects: var_export initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
var_export($obj);
|
||||
print "\n";
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
var_export($obj);
|
||||
print "\n";
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
\C::__set_state(array(
|
||||
'a' => 1,
|
||||
))
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
\C::__set_state(array(
|
||||
'a' => 1,
|
||||
))
|
62
Zend/tests/lazy_objects/initializeLazyObject.phpt
Normal file
62
Zend/tests/lazy_objects/initializeLazyObject.phpt
Normal file
@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
Lazy objects: ReflectionClass::initializeLazyObject()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($reflector?->initializeLazyObject($obj));
|
||||
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 1;
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$c = new C();
|
||||
$c->a = 1;
|
||||
return $c;
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
Initialized:
|
||||
bool(false)
|
||||
string(11) "initializer"
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
Initialized:
|
||||
bool(true)
|
||||
# Proxy:
|
||||
Initialized:
|
||||
bool(false)
|
||||
string(11) "initializer"
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
Initialized:
|
||||
bool(true)
|
51
Zend/tests/lazy_objects/initializeLazyObject_error.phpt
Normal file
51
Zend/tests/lazy_objects/initializeLazyObject_error.phpt
Normal file
@ -0,0 +1,51 @@
|
||||
--TEST--
|
||||
Lazy objects: ReflectionClass::initializeLazyObject() error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
try {
|
||||
var_dump($reflector?->initializeLazyObject($obj));
|
||||
} catch (Exception $e) {
|
||||
printf("%s\n", $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
throw new \Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
throw new \Exception('initializer exception');
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
bool(false)
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
bool(false)
|
||||
# Proxy:
|
||||
bool(false)
|
||||
string(11) "initializer"
|
||||
initializer exception
|
||||
bool(false)
|
@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Lazy objects: ReflectionClass::initializeLazyObject() on an initialized object is a no-op
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
var_dump($obj->a);
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($reflector?->initializeLazyObject($obj));
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 1;
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$c = new C();
|
||||
$c->a = 1;
|
||||
return $c;
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
bool(true)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(true)
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
bool(true)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(true)
|
@ -0,0 +1,164 @@
|
||||
--TEST--
|
||||
Lazy objects: initializer must return the right type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class B {
|
||||
public int $b;
|
||||
public function __construct() {
|
||||
$this->b = 1;
|
||||
}
|
||||
public function __destruct() {
|
||||
}
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
public int $b; // override
|
||||
}
|
||||
|
||||
class E extends B {
|
||||
public function __destruct() { // override
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost initializer must return NULL or no value:\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
return new stdClass;
|
||||
});
|
||||
|
||||
var_dump($obj);
|
||||
try {
|
||||
var_dump($obj->a);
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
var_dump($obj);
|
||||
|
||||
print "# Proxy initializer must return an instance of a compatible class:\n";
|
||||
print "## Valid cases:\n";
|
||||
|
||||
$tests = [
|
||||
[C::class, new C()],
|
||||
[C::class, new B()],
|
||||
[D::class, new B()],
|
||||
];
|
||||
|
||||
foreach ($tests as [$class, $instance]) {
|
||||
$obj = (new ReflectionClass($class))->newLazyProxy(function ($obj) use ($instance) {
|
||||
var_dump("initializer");
|
||||
$instance->b = 1;
|
||||
return $instance;
|
||||
});
|
||||
|
||||
printf("## %s vs %s\n", get_class($obj), is_object($instance) ? get_class($instance) : gettype($instance));
|
||||
var_dump($obj->b);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
print "## Invalid cases:\n";
|
||||
|
||||
$tests = [
|
||||
[C::class, new stdClass],
|
||||
[C::class, new DateTime()],
|
||||
[C::class, null],
|
||||
[C::class, new D()],
|
||||
[E::class, new B()],
|
||||
];
|
||||
|
||||
foreach ($tests as [$class, $instance]) {
|
||||
$obj = (new ReflectionClass($class))->newLazyProxy(function ($obj) use ($instance) {
|
||||
var_dump("initializer");
|
||||
return $instance;
|
||||
});
|
||||
|
||||
try {
|
||||
printf("## %s vs %s\n", get_class($obj), is_object($instance) ? get_class($instance) : gettype($instance));
|
||||
var_dump($obj->a);
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return $obj;
|
||||
});
|
||||
|
||||
try {
|
||||
printf("## %s vs itself\n", get_class($obj));
|
||||
var_dump($obj->a);
|
||||
} catch (\Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost initializer must return NULL or no value:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
TypeError: Lazy object initializer must return NULL or no value
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy initializer must return an instance of a compatible class:
|
||||
## Valid cases:
|
||||
## C vs C
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
## C vs B
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(B)#%d (1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
## D vs B
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
lazy proxy object(D)#%d (1) {
|
||||
["instance"]=>
|
||||
object(B)#%d (1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
## Invalid cases:
|
||||
## C vs stdClass
|
||||
string(11) "initializer"
|
||||
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.
|
||||
## C vs DateTime
|
||||
string(11) "initializer"
|
||||
TypeError: The real instance class DateTime 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.
|
||||
## C vs NULL
|
||||
string(11) "initializer"
|
||||
TypeError: Lazy proxy factory must return an instance of a class compatible with C, null returned
|
||||
## C vs D
|
||||
string(11) "initializer"
|
||||
TypeError: The real instance class D 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.
|
||||
## E vs B
|
||||
string(11) "initializer"
|
||||
TypeError: The real instance class B is not compatible with the proxy class E. 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.
|
||||
## C vs itself
|
||||
string(11) "initializer"
|
||||
Error: Lazy proxy factory must return a non-lazy object
|
51
Zend/tests/lazy_objects/invalid_options.phpt
Normal file
51
Zend/tests/lazy_objects/invalid_options.phpt
Normal file
@ -0,0 +1,51 @@
|
||||
--TEST--
|
||||
Lazy objects: invalid options
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a = 1;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
try {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) { }, -1);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$obj = $reflector->newLazyProxy(function ($obj) { }, -1);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
// SKIP_DESTRUCTOR is only allowed on resetAsLazyProxy()
|
||||
$obj = $reflector->newLazyGhost(function ($obj) { }, ReflectionClass::SKIP_DESTRUCTOR);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
$obj = new C();
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyGhost($obj, function ($obj) { }, -1);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyProxy($obj, function ($obj) { }, -1);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
ReflectionException: ReflectionClass::newLazyGhost(): Argument #2 ($options) contains invalid flags
|
||||
ReflectionException: ReflectionClass::newLazyProxy(): Argument #2 ($options) contains invalid flags
|
||||
ReflectionException: ReflectionClass::newLazyGhost(): Argument #2 ($options) does not accept ReflectionClass::SKIP_DESTRUCTOR
|
||||
ReflectionException: ReflectionClass::resetAsLazyGhost(): Argument #3 ($options) contains invalid flags
|
||||
ReflectionException: ReflectionClass::resetAsLazyProxy(): Argument #3 ($options) contains invalid flags
|
49
Zend/tests/lazy_objects/isUninitializedLazyObject.phpt
Normal file
49
Zend/tests/lazy_objects/isUninitializedLazyObject.phpt
Normal file
@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Lazy objects: ReflectionClass::isUninitializedLazyObject()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s\n", $name);
|
||||
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj->a);
|
||||
var_dump($reflector->isUninitializedLazyObject($obj));
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 1;
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj = new C();
|
||||
$obj->a = 1;
|
||||
return $obj;
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
# Ghost
|
||||
bool(true)
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
bool(false)
|
||||
# Proxy
|
||||
bool(true)
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
bool(false)
|
75
Zend/tests/lazy_objects/isset_hooked_may_initialize.phpt
Normal file
75
Zend/tests/lazy_objects/isset_hooked_may_initialize.phpt
Normal file
@ -0,0 +1,75 @@
|
||||
--TEST--
|
||||
Lazy objects: hooked property isset initializes object if hook observes object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a {
|
||||
get { return $this->a; }
|
||||
set($value) { $this->a = $value; }
|
||||
}
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump(isset($obj->a));
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
bool(true)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
64
Zend/tests/lazy_objects/isset_hooked_may_not_initialize.phpt
Normal file
64
Zend/tests/lazy_objects/isset_hooked_may_not_initialize.phpt
Normal file
@ -0,0 +1,64 @@
|
||||
--TEST--
|
||||
Lazy objects: hooked property isset may does not initialize object if hook does not observe object state
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a {
|
||||
get { return 1; }
|
||||
set($value) { }
|
||||
}
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump(isset($obj->a));
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
bool(true)
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
bool(true)
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
72
Zend/tests/lazy_objects/isset_initializes.phpt
Normal file
72
Zend/tests/lazy_objects/isset_initializes.phpt
Normal file
@ -0,0 +1,72 @@
|
||||
--TEST--
|
||||
Lazy objects: property isset initializes object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct(int $a) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = $a;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
var_dump(isset($obj->a));
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(1);
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(1);
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
bool(true)
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
69
Zend/tests/lazy_objects/jit_assign_obj_dynamic.phpt
Normal file
69
Zend/tests/lazy_objects/jit_assign_obj_dynamic.phpt
Normal file
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ with dynamic prop
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$obj->a = 2;
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
int(3)
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
int(3)
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
70
Zend/tests/lazy_objects/jit_assign_obj_op_dynamic.phpt
Normal file
70
Zend/tests/lazy_objects/jit_assign_obj_op_dynamic.phpt
Normal file
@ -0,0 +1,70 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ_OP with dynamic prop
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
$this->b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$obj->a += 1;
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
int(3)
|
||||
["a"]=>
|
||||
int(2)
|
||||
}
|
||||
}
|
69
Zend/tests/lazy_objects/jit_assign_obj_op_prop_info.phpt
Normal file
69
Zend/tests/lazy_objects/jit_assign_obj_op_prop_info.phpt
Normal file
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ_OP with known prop_info
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$obj->a += 1;
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(3)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(3)
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ_OP with unknown prop info
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
// Private prop so that prop_info is not inferred
|
||||
private int $a;
|
||||
public int $b;
|
||||
function __construct() {
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
}
|
||||
function test(object $obj) {
|
||||
$obj->a += 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
// Call via reflection to avoid inlining.
|
||||
// - test() handlers are executed once, and prime the runtime cache
|
||||
// - On subsequent calls, the JIT'ed code is used, and we enter the valid runtime cache path
|
||||
$reflector->getMethod('test')->invoke($obj, $obj);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ_OP with unknown prop info untyped
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
// Private prop so that prop_info is not inferred
|
||||
private int $a;
|
||||
public int $b;
|
||||
function __construct() {
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
}
|
||||
function test(object $obj) {
|
||||
$obj->a += 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
// Call via reflection to avoid inlining.
|
||||
// - test() handlers are executed once, and prime the runtime cache
|
||||
// - On subsequent calls, the JIT'ed code is used, and we enter the valid runtime cache path
|
||||
$reflector->getMethod('test')->invoke($obj, $obj);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
69
Zend/tests/lazy_objects/jit_assign_obj_prop_info.phpt
Normal file
69
Zend/tests/lazy_objects/jit_assign_obj_prop_info.phpt
Normal file
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ with known prop_info
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $a;
|
||||
public int $b = 1;
|
||||
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump($obj);
|
||||
$obj->a = 2;
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
lazy ghost object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(3)
|
||||
}
|
||||
# Proxy:
|
||||
lazy proxy object(C)#%d (0) {
|
||||
["b"]=>
|
||||
uninitialized(int)
|
||||
}
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
lazy proxy object(C)#%d (1) {
|
||||
["instance"]=>
|
||||
object(C)#%d (2) {
|
||||
["a"]=>
|
||||
int(2)
|
||||
["b"]=>
|
||||
int(3)
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ with unknown prop info
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
// Private prop so that prop_info is not inferred
|
||||
private int $a;
|
||||
public int $b;
|
||||
function __construct() {
|
||||
$this->b = 2;
|
||||
}
|
||||
function test(object $obj) {
|
||||
$obj->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
// Call via reflection to avoid inlining.
|
||||
// - test() handlers are executed once, and prime the runtime cache
|
||||
// - On subsequent calls, the JIT'ed code is used, and we enter the valid runtime cache path
|
||||
$reflector->getMethod('test')->invoke($obj, $obj);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Lazy objects: JIT: ASSIGN_OBJ with unknown prop info untyped
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
// Private prop so that prop_info is not inferred
|
||||
private int $a;
|
||||
public int $b;
|
||||
function __construct() {
|
||||
$this->b = 2;
|
||||
}
|
||||
function test(object $obj) {
|
||||
$obj->a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
// Call via reflection to avoid inlining.
|
||||
// - test() handlers are executed once, and prime the runtime cache
|
||||
// - On subsequent calls, the JIT'ed code is used, and we enter the valid runtime cache path
|
||||
$reflector->getMethod('test')->invoke($obj, $obj);
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(C)#%d (2) {
|
||||
["a":"C":private]=>
|
||||
int(1)
|
||||
["b"]=>
|
||||
int(2)
|
||||
}
|
49
Zend/tests/lazy_objects/json_encode_dynamic_props.phpt
Normal file
49
Zend/tests/lazy_objects/json_encode_dynamic_props.phpt
Normal file
@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Lazy objects: json_encode with dynamic props on initialized object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a = 1;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s\n", $name);
|
||||
|
||||
var_dump(json_decode(json_encode($obj)));
|
||||
}
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
$obj->dyn = 1;
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function () {
|
||||
$c = new C();
|
||||
$c->dyn = 1;
|
||||
return $c;
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost
|
||||
object(stdClass)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["dyn"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy
|
||||
object(stdClass)#%d (2) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
["dyn"]=>
|
||||
int(1)
|
||||
}
|
66
Zend/tests/lazy_objects/markLazyObjectAsInitialized.phpt
Normal file
66
Zend/tests/lazy_objects/markLazyObjectAsInitialized.phpt
Normal file
@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
Lazy objects: markLazyObjectAsInitialized() initializes properties to their default value and skips initializer
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public int $a = 1;
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
printf("markLazyObjectAsInitialized(true) returns \$obj:\n");
|
||||
var_dump($reflector?->markLazyObjectAsInitialized($obj) === $obj);
|
||||
|
||||
printf("Initialized:\n");
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->a = 1;
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$c = new C();
|
||||
$c->a = 1;
|
||||
return $c;
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
Initialized:
|
||||
bool(false)
|
||||
markLazyObjectAsInitialized(true) returns $obj:
|
||||
bool(true)
|
||||
Initialized:
|
||||
bool(true)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
||||
# Proxy:
|
||||
Initialized:
|
||||
bool(false)
|
||||
markLazyObjectAsInitialized(true) returns $obj:
|
||||
bool(true)
|
||||
Initialized:
|
||||
bool(true)
|
||||
object(C)#%d (1) {
|
||||
["a"]=>
|
||||
int(1)
|
||||
}
|
96
Zend/tests/lazy_objects/realize.phpt
Normal file
96
Zend/tests/lazy_objects/realize.phpt
Normal file
@ -0,0 +1,96 @@
|
||||
--TEST--
|
||||
Lazy objects: Object is not lazy anymore if all props have been assigned a value
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class B {
|
||||
private readonly string $b;
|
||||
|
||||
public function __construct() {
|
||||
$this->b = 'b';
|
||||
}
|
||||
}
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C extends B {
|
||||
public string $a;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->a = 'a';
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 'a1');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
// Should not count a second prop initialization
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 'a2');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
try {
|
||||
// Should not count a prop initialization
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, new stdClass);
|
||||
} catch (Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
// Should not count a prop initialization
|
||||
//$reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, 'dynamic B');
|
||||
//var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
(new ReflectionProperty(B::class, 'b'))->setRawValueWithoutLazyInitialization($obj, 'b');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
TypeError: Cannot assign stdClass to property C::$a of type string
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["b":"B":private]=>
|
||||
string(1) "b"
|
||||
["a"]=>
|
||||
string(2) "a2"
|
||||
}
|
||||
# Proxy:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
TypeError: Cannot assign stdClass to property C::$a of type string
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["b":"B":private]=>
|
||||
string(1) "b"
|
||||
["a"]=>
|
||||
string(2) "a2"
|
||||
}
|
78
Zend/tests/lazy_objects/realize_no_props.phpt
Normal file
78
Zend/tests/lazy_objects/realize_no_props.phpt
Normal file
@ -0,0 +1,78 @@
|
||||
--TEST--
|
||||
Lazy objects: Object with no props is never lazy
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {}
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class D {}
|
||||
|
||||
function test(string $name, object $obj, object $obj2, object $obj3) {
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump((new ReflectionClass($obj::class))->isUninitializedLazyObject($obj));
|
||||
var_dump($obj);
|
||||
|
||||
var_dump((new ReflectionClass($obj2::class))->isUninitializedLazyObject($obj2));
|
||||
var_dump($obj2);
|
||||
|
||||
var_dump((new ReflectionClass($obj3::class))->isUninitializedLazyObject($obj3));
|
||||
var_dump($obj3);
|
||||
}
|
||||
|
||||
$obj = new C();
|
||||
(new ReflectionClass($obj))->resetAsLazyGhost($obj, function ($obj) {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
$obj2 = new D();
|
||||
$obj2->dynamic = 'value';
|
||||
(new ReflectionClass($obj2))->resetAsLazyGhost($obj2, function ($obj2) {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
$obj3 = (new ReflectionClass(C::class))->newLazyGhost(function () {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
test('Ghost', $obj, $obj2, $obj3);
|
||||
|
||||
$obj = new C();
|
||||
(new ReflectionClass($obj))->resetAsLazyProxy($obj, function ($obj) {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
$obj2 = new D();
|
||||
$obj2->dynamic = 'value';
|
||||
(new ReflectionClass($obj2))->resetAsLazyProxy($obj2, function ($obj2) {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
$obj3 = (new ReflectionClass(C::class))->newLazyGhost(function () {
|
||||
var_dump("initializer");
|
||||
});
|
||||
|
||||
test('Proxy', $obj, $obj2, $obj3);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
bool(false)
|
||||
object(C)#%d (0) {
|
||||
}
|
||||
bool(false)
|
||||
object(D)#%d (0) {
|
||||
}
|
||||
bool(false)
|
||||
object(C)#%d (0) {
|
||||
}
|
||||
# Proxy:
|
||||
bool(false)
|
||||
object(C)#%d (0) {
|
||||
}
|
||||
bool(false)
|
||||
object(D)#%d (0) {
|
||||
}
|
||||
bool(false)
|
||||
object(C)#%d (0) {
|
||||
}
|
91
Zend/tests/lazy_objects/realize_proxy_overridden.phpt
Normal file
91
Zend/tests/lazy_objects/realize_proxy_overridden.phpt
Normal file
@ -0,0 +1,91 @@
|
||||
--TEST--
|
||||
Lazy objects: Object is not lazy anymore if all props have been assigned a value (overridden prop)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class B {
|
||||
public readonly string $b;
|
||||
|
||||
public function __construct() {
|
||||
$this->b = 'b';
|
||||
}
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
public string $a;
|
||||
public readonly string $b;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->a = 'a';
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 'a1');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
// Should not count a second prop initialization
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 'a2');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
try {
|
||||
// Should not count a prop initialization
|
||||
$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, new stdClass);
|
||||
} catch (Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
(new ReflectionProperty(B::class, 'b'))->setRawValueWithoutLazyInitialization($obj, 'b');
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
TypeError: Cannot assign stdClass to property C::$a of type string
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
string(1) "b"
|
||||
["a"]=>
|
||||
string(2) "a2"
|
||||
}
|
||||
# Proxy:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
TypeError: Cannot assign stdClass to property C::$a of type string
|
||||
bool(true)
|
||||
object(C)#%d (2) {
|
||||
["b"]=>
|
||||
string(1) "b"
|
||||
["a"]=>
|
||||
string(2) "a2"
|
||||
}
|
101
Zend/tests/lazy_objects/realize_skipped.phpt
Normal file
101
Zend/tests/lazy_objects/realize_skipped.phpt
Normal file
@ -0,0 +1,101 @@
|
||||
--TEST--
|
||||
Lazy objects: Object is not lazy anymore if all props have been skipped
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class B {
|
||||
private readonly string $b;
|
||||
|
||||
public function __construct() {
|
||||
$this->b = 'b';
|
||||
}
|
||||
}
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class C extends B {
|
||||
public string $a;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->a = 'a';
|
||||
}
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# %s:\n", $name);
|
||||
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
$reflector->getProperty('a')->skipLazyInitialization($obj);
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
// Should not count a second prop initialization
|
||||
$reflector->getProperty('a')->skipLazyInitialization($obj);
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
try {
|
||||
// Should not count a prop initialization
|
||||
$reflector->getProperty('xxx')->skipLazyInitialization($obj);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
// Should not count a prop initialization
|
||||
$reflector->getProperty('b')->skipLazyInitialization($obj);
|
||||
} catch (ReflectionException $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
(new ReflectionProperty(B::class, 'b'))->skipLazyInitialization($obj);
|
||||
var_dump(!$reflector->isUninitializedLazyObject($obj));
|
||||
|
||||
var_dump($obj);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
ReflectionException: Property C::$xxx does not exist
|
||||
ReflectionException: Property C::$b does not exist
|
||||
bool(true)
|
||||
object(C)#%d (0) {
|
||||
["b":"B":private]=>
|
||||
uninitialized(string)
|
||||
["a"]=>
|
||||
uninitialized(string)
|
||||
}
|
||||
# Proxy:
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
ReflectionException: Property C::$xxx does not exist
|
||||
ReflectionException: Property C::$b does not exist
|
||||
bool(true)
|
||||
object(C)#%d (0) {
|
||||
["b":"B":private]=>
|
||||
uninitialized(string)
|
||||
["a"]=>
|
||||
uninitialized(string)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
Lazy objects: resetAsLazy*() accept a sub-class of the reflected class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public $a;
|
||||
}
|
||||
class B extends A {}
|
||||
|
||||
class C {}
|
||||
|
||||
$reflector = new ReflectionClass(A::class);
|
||||
|
||||
$reflector->resetAsLazyGhost(new A(), function () {});
|
||||
$reflector->resetAsLazyGhost(new B(), function () {});
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyGhost(new C(), function () {});
|
||||
} catch (TypeError $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
TypeError: ReflectionClass::resetAsLazyGhost(): Argument #1 ($object) must be of type A, C given
|
||||
==DONE==
|
56
Zend/tests/lazy_objects/reset_as_lazy_already_exception.phpt
Normal file
56
Zend/tests/lazy_objects/reset_as_lazy_already_exception.phpt
Normal file
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
Lazy objects: resetAsLazy*() on already lazy object is not allowed
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C extends stdClass {
|
||||
public int $a;
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
printf("# Ghost:\n");
|
||||
|
||||
$obj = new C();
|
||||
$reflector->resetAsLazyGhost($obj, function () {});
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyGhost($obj, function ($obj) {
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
printf("# Proxy:\n");
|
||||
|
||||
$obj = new C();
|
||||
$reflector->resetAsLazyProxy($obj, function () {});
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyProxy($obj, function ($obj) {
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
$obj = new C();
|
||||
$reflector->resetAsLazyProxy($obj, function () {
|
||||
return new C();
|
||||
});
|
||||
$reflector->initializeLazyObject($obj);
|
||||
|
||||
try {
|
||||
$reflector->resetAsLazyProxy($obj, function ($obj) {
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
ReflectionException: Object is already lazy
|
||||
# Proxy:
|
||||
ReflectionException: Object is already lazy
|
||||
==DONE==
|
61
Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt
Normal file
61
Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt
Normal file
@ -0,0 +1,61 @@
|
||||
--TEST--
|
||||
Lazy objects: resetAsLazy*() calls destructor of pre-existing object
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public readonly int $a;
|
||||
|
||||
public function __construct() {
|
||||
$this->a = 1;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
var_dump(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
|
||||
$obj = new C();
|
||||
print "In makeLazy\n";
|
||||
$reflector->resetAsLazyGhost($obj, function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
print "After makeLazy\n";
|
||||
|
||||
var_dump($obj->a);
|
||||
$obj = null;
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = new C();
|
||||
print "In makeLazy\n";
|
||||
$reflector->resetAsLazyProxy($obj, function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
print "After makeLazy\n";
|
||||
|
||||
var_dump($obj->a);
|
||||
$obj = null;
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
In makeLazy
|
||||
string(13) "C::__destruct"
|
||||
After makeLazy
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
string(13) "C::__destruct"
|
||||
# Proxy:
|
||||
In makeLazy
|
||||
string(13) "C::__destruct"
|
||||
After makeLazy
|
||||
string(11) "initializer"
|
||||
int(1)
|
||||
string(13) "C::__destruct"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user