php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #61945 array returned from __get method gives a notice when trying to change a value
Submitted: 2012-05-04 22:33 UTC Modified: 2021-11-10 11:07 UTC
From: sevenrow2 at yahoo dot com Assigned:
Status: Open Package: Scripting Engine problem
PHP Version: 5.4.2 OS: Windows 7
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: sevenrow2 at yahoo dot com
New email:
PHP Version: OS:

 

 [2012-05-04 22:33 UTC] sevenrow2 at yahoo dot com
Description:
------------
I'm using the latest downloadable version of PHP 5.4.2.

See the following code (this code works):
-----------

<?php

class A {
  private $vars;

  public function __get($name) {
    if (!isset($this->vars[$name])) {
      $arrObj = new B();
      $this->vars[$name] = $arrObj;
    }

    $obj = $this->vars[$name];    
    return $obj;    
  }

  
}

class B {
  public $rolename = 'foo';
}


$a = new A;
var_dump($a);
echo $a->role->rolename.PHP_EOL;
$a->role->rolename = 'test';

echo $a->role->rolename;

?>

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

What happends in this code is that i create a simple object "A". From that object i try to get the 'role' property. It doesn't exist, so the magic __get() function is called.

In there i create a B() object and i return the instance of that object. Right after that i'm trying to access the 'rolename' property of the 'B()' object:

echo $a->role->rolename.PHP_EOL;
$a->role->rolename = 'test';

This works. It successfully echo's the rolename and changes it after that.

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

The problem occurs when i return an array with objects:

<?php

class A {
  private $vars;

  public function __get($name) {
    if (!isset($this->vars[$name])) {
      $arrObj = array();
      $arrObj[] = new B();
      $arrObj[] = new B();
      $this->vars[$name] = $arrObj;
    }

    return $this->vars[$name];
  }

  
}

class B {
  public $rolename = 'foo';
}


$a = new A;
var_dump($a);
echo $a->role[0]->rolename.PHP_EOL;
$a->role[0]->rolename = 'test';

echo $a->role[0]->rolename;
?>

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

This code gives me the following notice:

"Notice: Indirect modification of overloaded property A::$role has no effect"

Strangely enough it tells me that i can't change the property any more. Or better yet, it has no effect. The only difference is, is that i get the object from an array.

The weird thing is though, that it DOES alter the value of the property, regardless of the notice.

I think the notice shouldn't be displayed in this case. 

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

class A {
  private $vars;

  public function __get($name) {
    if (!isset($this->vars[$name])) {
      $arrObj = array();
      $arrObj[] = new B();
      $arrObj[] = new B();
      $this->vars[$name] = $arrObj;
    }

    return $this->vars[$name];
  }

  
}

class B {
  public $rolename = 'foo';
}


$a = new A;
var_dump($a);
echo $a->role[0]->rolename.PHP_EOL;
$a->role[0]->rolename = 'test';

echo $a->role[0]->rolename;
?>

Expected result:
----------------
I expected that $a->role[0]->rolename = 'test'; simply changed the value of that property, but it generates an unexpected 'notice'.

But: echo $a->role[0]->rolename; does show me that the property was actually changed, regardless of the notice which tells that it can't be changed.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-05-07 10:21 UTC] nikic@php.net
You should define __get to return by reference:

    public function &__get($name) { ... }

Modifications of the type $foo->x[0] = 'y' (where ->x is a magic property) happen by executing roughly the following code:

    $array =& $foo->x;
    $array[0] = 'y';

If $foo->x is not a reference here though, $array[0] = 'y' will only be able to change the copied array, not the original one.

In your case you still see the change due to a lucky combination of arrays and objects. You aren't actually modifying the array, but only the object and objects behave reference-like by themselves.

---

I'd be inclined to close this as Not A Bug, but there is actually some kind of bug in here: PHP should see that $foo->x[0]->y = 'z' does not change the returned array, so no notice should appear. But I'm not sure whether that's fixable.
 [2012-05-20 22:52 UTC] felipe@php.net
-Summary: array returned from __get method gices a notice when trying to change a value +Summary: array returned from __get method gives a notice when trying to change a value
 [2017-09-27 15:45 UTC] cmb@php.net
-Package: Dynamic loading +Package: Scripting Engine problem
 [2021-11-10 11:07 UTC] cmb@php.net
To my surprise, <https://3v4l.org/iC3Ji> triggers the notice only
as of PHP 8.0.0.  What am I missing?
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 05:01:29 2024 UTC