Bug #72177 [Com]: Scope issue in __destruct after ReflectionProperty::setValue()

From: Date: Thu, 12 May 2016 21:43:35 +0000
Subject: Bug #72177 [Com]: Scope issue in __destruct after ReflectionProperty::setValue()
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-201050@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=72177&edit=1

 ID:                 72177
 Comment by:         ocramius at gmail dot com
 Reported by:        lbarnaud@php.net
 Summary:            Scope issue in __destruct after
                     ReflectionProperty::setValue()
 Status:             Assigned
 Type:               Bug
 Package:            Reflection related
 PHP Version:        7.0Git-2016-05-09 (Git)
 Assigned To:        dmitry
 Block user comment: N
 Private report:     N

 New Comment:

Related: I got similar failures when accessing object state via __get, when
__get is triggered by reflection, as the scope with which __get is called
is wrong.
Specifically, something like following:

=====foo.php======
<?php

class Foo
{
    private $bar = 'bar';

    public function __construct()
    {
        unset($this->bar);
    }
}

class Bar extends Foo
{
    private $baz = 'baz';
    private static $tab = 'tab';

    public function __get(string $name)
    {
        var_dump($this->baz);
        var_dump(self::$tab);
        return $name;
    }
}

$r = new ReflectionProperty(Foo::class, 'bar');

$r->setAccessible(true);

var_dump($r->getValue(new Bar));
=====/foo.php======

This will work in 7.0.0~7.0.7 (see https://3v4l.org/dQlsf), but
will crash on master (7.1.0-DEV), with something like following:

=====OUTPUT======
NULL

Fatal error: Uncaught Error: Cannot access private property Bar::$tab in foo.php:21
Stack trace:
#0 foo.php(20): Bar->__get('baz')
#1 [internal function]: Bar->__get('bar')
#2 foo.php(30): ReflectionProperty->getValue(Object(Bar))
#3 {main}
  thrown in /Users/ocramius/Documents/Projects/ProxyManager/foo.php on line 21
=====/OUTPUT======


Previous Comments:
------------------------------------------------------------------------
[2016-05-12 21:23:56] nikic@php.net

Note that this affects not only __destruct(), but also __get() and other magic. E.g. the call https://github.com/Ocramius/ProxyManager/blob/6e15877fc03cc211ce798d497a21208a44f7b726/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php#L403
results in an access level error in https://gist.github.com/Ocramius/e4f481dd1d4f93adffb0646a780d1217#file-foo5731cbdb7cfb2968834795-php-L125.

------------------------------------------------------------------------
[2016-05-12 15:48:59] nikic@php.net

Fallout from the EG(scope) removal. Now EG(fake_scope) will continue shadowing the scope, even
though we might reenter a differently scoped method.

This looks a bit tricky... maybe we should just avoid the fake scoping altogether and add an extra
scope argument to the property handlers?

------------------------------------------------------------------------
[2016-05-09 13:28:50] lbarnaud@php.net

Description:
------------
When __destruct() is called, following a ReflectionProperty::setValue() call, scopes seem to be
wrong.

In the following reproducing script, the method Child::__destruct() fails to set
$this->bar to null, with the error Cannot access protected property
Child::$bar.

I can reproduce this in git master, but not in any version from 3v4l.org.

Test script:
---------------
<?php

class Child
{
    protected $bar;

    public function __destruct()
    {
        $this->bar = null;
    }
}

class Parnt
{
    protected $child;

    public function doSomething()
    {
        $this->child = new Child();

        $prop = new \ReflectionProperty($this, 'child');
        $prop->setAccessible(true);
        $prop->setValue($this, null);
    }
}

$p = new Parnt();
$p->doSomething();

echo "OK\n";

Expected result:
----------------
OK

Actual result:
--------------
Fatal error: Uncaught Error: Cannot access protected property Child::$bar in /tmp/x.php:9
Stack trace:
#0 [internal function]: Child->__destruct()
#1 /tmp/x.php(23): ReflectionProperty->setValue(Object(Parnt), NULL)
#2 /tmp/x.php(28): Parnt->doSomething()
#3 {main}
  thrown in /tmp/x.php on line 9


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=72177&edit=1


Thread (5 messages)

« previous php.bugs (#201050) next »