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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
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

Pull Requests

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: Thu Nov 21 20:01:29 2024 UTC