php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #55731 __get after __unset
Submitted: 2011-09-20 02:35 UTC Modified: 2013-01-18 21:50 UTC
From: 421034509 at qq dot com Assigned: stas (profile)
Status: Closed Package: Unknown/Other Function
PHP Version: 5.3.8 OS: windows xp
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: 421034509 at qq dot com
New email:
PHP Version: OS:

 

 [2011-09-20 02:35 UTC] 421034509 at qq dot com
Description:
------------
<?php
  class Example{
  private $p1;
  private $p2;
  function __construct($a){
  $this->p1=$a;
  }
  function __get($elmname){
  echo "Call_get()!"; //In order to follow the method __get()
  return $this->$elmname;
  }
  function __isset($name){
  return isset($this->$name);
  }
  function __unset($name){
  unset($this->$name);
  }
 }
 $example=new Example("v1","v2");  
 echo $example->p1."<br/>";
 var_dump(isset($example->p2));//p2 is uninitialized,the result is bool(false)
 echo $example->p2;//This time,__get is called by 1 time.
 unset($example->p1);
 var_dump(isset($example->p1));//Because of __unset,the result is bool(false)
 echo $example->p1;//Amazing!I call the __get just once,but it seems that the __get method has bean called two times!
?>

Expected result:
----------------
Call_get()! v1
bool(false) Call_get()!bool(false) Call_get()!

Actual result:
--------------
Call_get()! v1
bool(false) Call_get()!bool(false) Call_get()!Call_get()!

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-09-20 04:07 UTC] laruence@php.net
-Status: Open +Status: Bogus
 [2011-09-20 04:07 UTC] laruence@php.net
see blow, and I think you will find out the reason:
<?php
class Example{
    private $p1;
    function __construct($a){
        $this->p1=$a;
    }
    function __get($elmname){
        echo "Call_get()!"; //In order to follow the method __get()
    }
    function __unset($name){
        unset($this->$name);
    }
}
$example = new Example("v1");
unset($example->p1);
echo $example->p1;
 [2011-09-20 08:47 UTC] 421034509 at qq dot com
I want to know what happened after __unset.
$example->p1 has bean unset and $example->p2 is never set.
But isset($example->p1)) and isset($example->p2)) are all false.
Why $example->p2 call __get once but $example->p1 call __get twice?
What's the diference of the $example->p1 which has bean unset and the $example->p2 which is never set"?
 [2011-09-20 09:42 UTC] laruence@php.net
I have give you a example which will only call get once after unset, and okey, if you want a 
more specific explain, here we go:

1. you call $example->p1
2. zend vm call read_propery , in which internal will check property_info and socpe, since this 
is called out from example->ce, so get_property_info will deny and return NULL, then zend vm 
assume you are try to access a public "p1"()
3. so zend vm set a getter_guard for "p1" and call getter try to find "p1" (output 
"call_get()!")
4. in your custom getter, you attempt fetch $this->p1
5. in this case, you attempt to access p1 in example class scope , so get_property_info 
successed, and return a property info with name "\0Example\0p1"
5. zend vm try to fetch "\0Example\0p1" now, set a getter_guard for "\0Example\0p1"  and call 
getter again (output "call_get()!")
7. zend vm found call getter in getter(by gettter_guard which was setted in 5), then give up, 
and return.
 [2011-09-21 00:48 UTC] 421034509 at qq dot com
Thanks for your explain,but there is something I still can't understand.
In 2:what's the "called out from $Example->ce" mean?
(Sorry!My English is poor and I have studied PHP a short time)
Do you mean I call $example->p1 out of the class?
If so,I call $example->p2 out of the class too.
Then,in my custom getter, I attempt fetch $this->p2.
in this case, it attempt to access p2 in example class scope ,
why dosn't it call getter again?
Can you explain why $example->p2 call getter only once for me like before?
 [2011-09-21 03:32 UTC] laruence@php.net
p2 is a litte different, see blow:
1. you call $example->p2
2. zend vm call read_propery , in which internal will check property_info and socpe, since this 
is called out from example->ce, so get_property_info will deny and return NULL, then zend vm 
assume you are try to access a public "p2"()
3. so zend vm set a getter_guard for "p2" and call getter try to find "p2" (output 
"call_get()!")
4. in your custom getter, you fetch $example->p2
5. zend vm call read_property, the first begining is similar to the situation when you trying to fetch p1, but, zend vm 
find p2 in $example->properties(this it the key difference), then suceed and return.
 [2011-09-21 07:42 UTC] 421034509 at qq dot com
p1:
You mean __unset() made the p1 out of $example->properties?
When get_property_info successed,
zend vm return a property info with name "\0Example\0p1" 
but it can't find p1 in $example->properties ?
So zend vm try to fetch "\0Example\0p1" and set a getter_guard for "\0Example\0p1" and call getter again.
p2:
When get_property_info successed,
zend vm return a property info with name "\0Example\0p2" 
and p2 can be found in $example->properties.
So zend vm wont try to fetch "\0Example\0p1" but successed and return p2?
 [2011-09-21 08:07 UTC] laruence@php.net
kind of right, you can refer to zend_std_read_property(in 
Zend/zend_object_handlers.c) for more details. 

and btw, you seems to be a chinese(using qq email), so if you are interesting of 
php internal, plz look at laruence.com, I will write these down in chinese in 
couple of days
 [2011-09-21 09:02 UTC] 421034509 at qq dot com
Thank you very much!You are so kind and patient!
I see your blog!
It's great!
您也是中国人吗?
我现在大四,现在在北京学习!刚接触php!希望以后还能请教您!
 [2011-09-21 16:51 UTC] hytest at gmail dot com
If we add following code:

echo $example->p3;  

It still just call __get once. Why it doesn't call it twice? 
( one for "p3" and one for "\0example\0p3" ? )

In another word: what's the different between a property not defined and a unset 
private property?
 [2011-09-22 01:17 UTC] laruence@php.net
because there is no '\0Example\0p3' in property_info of  Example class entry
 [2011-09-22 01:18 UTC] 421034509 at qq dot com
When get_property_info successed in the Example class,
if the private property is defined but be unsetted,zend vm return a property info with name "\0Example\0propertyname",
but if the property is undefined(or defined but not be unsetted),zend vm return a property info with name "propertyname".
Is that true?
 [2011-09-22 01:28 UTC] laruence@php.net
No, if the property is private, and the getter is called out of call entry(means 
access a private property out of scope), zend vm will return NULL,  but 
zend_read_property will assume as a "public property name", since PHP is a 
flexible language.
 [2011-09-22 03:36 UTC] 421034509 at qq dot com
Thank you!I got it!
 [2012-11-08 08:13 UTC] ocramius at gmail dot com
I got pointed here after filing #63462 ( https://bugs.php.net/bug.php?id=63462 ).

Shouldn't the `getter_guard` be disabled for "\0Example\0p1: (laurence's example) when already in the context of `__get` (and similar logic for all the other magic methods)? 

I have absolutely no understanding of PHP internals about this, but I wouldn't expect the magic getter to work again for the same property when we already know it was the requested one.

This is actually a (quite problematic) bug for me since it denies any action on unset properties, which is a feature I need when working with proxies.

If I understand this correctly, at https://github.com/php/php-src/blob/6ba376f552238de643a665d355fd5e59c40315b5/Zend/zend_object_handlers.c#L559-572 the guard is used. I don't understand why `guard->in_set` isn't checked the second time though. Does `zend_get_property_guard` retrieve a different item for different `property_info`?
 [2013-01-18 21:50 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.

fix for bug #63462 should also fix this one.
 [2013-01-18 21:50 UTC] stas@php.net
-Status: Not a bug +Status: Closed -Assigned To: +Assigned To: stas
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 14:01:28 2024 UTC