-
-
Notifications
You must be signed in to change notification settings - Fork 193
Remove direct reference to value holder property #396
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
Conversation
|
Green btw :) |
|
There are two issues with this patch, sadly. Here's the pseudo-code representing the issue: $proxy = $theFactory->createProxy(
SomeClass::class,
function (& $wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
$initializer = null;
$wrappedObject = new HeavyComplexObject();
// return false; purposely omitting the `return` value will break the initialization
}
); $proxy = $theFactory->createProxy(
SomeClass::class,
function (& $wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
$initializer = null;
$wrappedObject = new HeavyComplexObject();
$proxy->setSomething('someValue'); // infinite initialization loop
return true;
}
); |
|
I'll work on alternate generated code in a bit. Will likely need to move away from a single boolean expression. |
| }); | ||
|
|
||
| $proxy->compress('Lazy '); | ||
| $dummy->reference = 123; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is misuse of a reference... Not much I can do against it: that's a bug in the consumer code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$proxy->compress('Lazy ');
$foo = 123;
$dummy->reference = & $foo;
$proxy->compress('Loaded!')This is how it SHOULD be done.
|
According to https://github.com/Ocramius/ProxyManager/pull/396/files#r151069163, the test case is invalid. Something else is going on - the current patch seems to mitigate a symptom of some other root cause. |
|
In the current status, this issue is Until a proper test scenario is found (may be tricky), this is gonna stay in a limbo. |
|
Test case updated, you might find it more practical now?
I'm not sure: the generated code is independent of the return value of the closure
Not an infinite loop: the initializer being reseted, this cannot loop. I think this case is hit because of a bug in OPcache, that forgets to clean the "is_ref" state on the local variable. I cannot reproduce it on a simple case (but the linked issue contains a reproducer using Docker, on 7.1-stable). FYI, I already fixed that in Symfony, by patching the generated code (we're free from any issue with this "Call to a member function doFoo() on null" since the initializer is also generated). |
| ?> | ||
| --EXPECT-- | ||
| 123 | ||
| 234 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
echoes "123" twice without the patch
|
@nicolas-grekas thanks, new test looks much better! |
|
Note: this will need to be reproduced locally with/without opcache. I'll try reducing it to a minimal test case for php-src. |
|
Dug further - this is not an opcache bug, as the code behaves the same way with or without opcache enabled: --TEST--
Verifies that lazy loading value holder cannot be turned to a reference
?>
--FILE--
<?php
class Proxied
{
public $publicProperty;
}
class Proxy extends Proxied
{
/** @var Proxied|null */
private $wrapped;
/** @var callable|null */
private $initializer;
public static function staticProxyConstructor(callable $initializer) : self
{
$instance = new self();
unset($instance->publicProperty);
$instance->initializer = $initializer;
return $instance;
}
public function & __get($name)
{
$this->initializer && ($this->initializer)($this->wrapped, $this->initializer);
return $this->wrapped->$name;
}
public function __set($name, $value)
{
$this->initializer && ($this->initializer)($this->wrapped, $this->initializer);
return ($this->wrapped->$name = $value);
}
public function __isset($name)
{
$this->initializer && ($this->initializer)($this->wrapped, $this->initializer);
return isset($this->wrapped->$name);
}
public function __clone()
{
$this->initializer && ($this->initializer)($this->wrapped, $this->initializer);
$this->wrapped = clone $this->wrapped;
}
}
$dummy = new stdClass();
$proxy = Proxy::staticProxyConstructor(function (& $wrapped, & $initializer) use ($dummy) {
$initializer = null;
$wrapped = new Proxied();
$dummy->reference = & $wrapped;
});
$proxy->publicProperty = 123;
$clone = clone $proxy;
$clone->publicProperty = 234;
echo $proxy->publicProperty, "\n";
echo $clone->publicProperty, "\n";
?>
--EXPECT--
123
234 |
|
Reduced the examples even further and posted at https://bugs.php.net/bug.php?id=75629 |
|
@nicolas-grekas is this reproducible only with |
|
According to @nikic (https://chat.stackoverflow.com/transcript/message/40315447#40315447):
@nicolas-grekas this seems to be expected behavior. In this specific case, the reference needs to be broken in the |
Fixes #394