Skip to content

[RFC] Lazy Objects #15019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8347ada
Lazy objects
arnaud-lb May 19, 2023
c5901fc
Test cleanup
arnaud-lb Jul 25, 2024
36c70ee
Test cleanup
arnaud-lb Jul 25, 2024
80b4228
Fix build
arnaud-lb Aug 7, 2024
0832ea2
Hooks
arnaud-lb Aug 7, 2024
0b2f632
Fix windows build
arnaud-lb Aug 10, 2024
6460680
Fix test
arnaud-lb Aug 13, 2024
a674d55
JIT
arnaud-lb Aug 13, 2024
627de1f
Remove TODO
arnaud-lb Aug 13, 2024
a724938
Expose initializer zv to gc
arnaud-lb Aug 13, 2024
ac439e8
Add test
arnaud-lb Aug 13, 2024
bd03b05
Internal param name consistency with api
arnaud-lb Aug 13, 2024
ed7d39c
Update comment
arnaud-lb Aug 16, 2024
56d10db
Add assertion
arnaud-lb Aug 16, 2024
a509e91
Cleanup
arnaud-lb Aug 16, 2024
e8d1b9d
Rename zend_object.flags
arnaud-lb Aug 20, 2024
d7bf624
Rename zend_get_properties_no_init
arnaud-lb Aug 20, 2024
f70412c
De-dup
arnaud-lb Aug 20, 2024
89c8656
Do not serialize as N;
arnaud-lb Aug 20, 2024
c9eec20
WS / naming
arnaud-lb Aug 20, 2024
07331a4
Use zend_argument_error
arnaud-lb Aug 20, 2024
2c740fd
De-dup
arnaud-lb Aug 20, 2024
6f0a927
Improve naming: rename ce to reflection_ce
arnaud-lb Aug 20, 2024
3a3b755
RETURN_THROWS / RETURN_NULL
arnaud-lb Aug 20, 2024
0e24294
Fallback in case zend_std_write_property is overridden
arnaud-lb Aug 20, 2024
2a57a75
Remove unreachable condition
arnaud-lb Aug 20, 2024
d92db6e
Extract common logic for setRawValue / setRawValueWithoutLazyInitiali…
arnaud-lb Aug 20, 2024
81bfdb1
Do not set prop as lazy after setRawValueWithoutLazyInitialization fa…
arnaud-lb Aug 20, 2024
eace710
Extract common logic between setRawValueWithoutLazyInitialization / s…
arnaud-lb Aug 20, 2024
db0bf61
Use ZVAL_COPY_PROP
arnaud-lb Aug 20, 2024
e5af928
Add comment / extract logic
arnaud-lb Aug 20, 2024
7b4545f
Add test
arnaud-lb Aug 20, 2024
8415e9d
Rename IS_OBJ_LAZY -> IS_OBJ_LAZY_UNINITIALIZED
arnaud-lb Aug 20, 2024
e85595c
Early return
arnaud-lb Aug 20, 2024
bf20f0a
Fix null pointer deref
arnaud-lb Aug 20, 2024
f886726
Fix comparison of lazy objects
arnaud-lb Aug 20, 2024
f8df7ae
Skip zend_std_get_properties()
arnaud-lb Aug 20, 2024
23e1a4b
Remove dead code for clone handling
arnaud-lb Aug 20, 2024
33acce5
newLazy*() do not accept SKIP_DESTRUCTOR
arnaud-lb Aug 20, 2024
e25d16a
Make functions static when possible
arnaud-lb Aug 20, 2024
a31e777
Simplify assertions
arnaud-lb Aug 20, 2024
6cf6496
Prevent warning
arnaud-lb Aug 20, 2024
8841964
Check final property
arnaud-lb Aug 20, 2024
77fe4fc
Avoid redundant addref/delref
arnaud-lb Aug 20, 2024
d0c9e36
Prevent making object lazy during its initialization
arnaud-lb Aug 20, 2024
9248c39
Improve error message
arnaud-lb Aug 20, 2024
4a0eb5f
Fix build
arnaud-lb Aug 20, 2024
b960778
Rename constants
arnaud-lb Aug 20, 2024
174fe00
CS
arnaud-lb Aug 20, 2024
7f2190e
Generated file
arnaud-lb Aug 21, 2024
5864de3
Do not inline slow path
arnaud-lb Aug 21, 2024
d1a10ac
Ensure separation
arnaud-lb Aug 21, 2024
3e5c95c
Simplify
arnaud-lb Aug 21, 2024
c332fa5
Add assertion
arnaud-lb Aug 28, 2024
9114a71
Prevent compiler warning
arnaud-lb Aug 28, 2024
3b3a200
Simplify
arnaud-lb Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions Zend/tests/lazy_objects/clone_calls___clone_once.phpt
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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 changes: 73 additions & 0 deletions Zend/tests/lazy_objects/clone_initialized.phpt
Original file line number Diff line number Diff line change
@@ -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 changes: 56 additions & 0 deletions Zend/tests/lazy_objects/clone_initializer_exception.phpt
Original file line number Diff line number Diff line change
@@ -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) {
}
Loading
Loading