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
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: 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

Pull Requests

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: Thu Dec 26 22:01:28 2024 UTC