|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2004-05-19 12:34 UTC] dennis at inmarket dot lviv dot ua
Description:
------------
"Cannot access undefined property for object with overloaded property access" is an error when I try to set a property of an instance, which, in turn, is a property of another instance of an overloaded class, i.e. $a->x->y = 2 triggers the error if $a is an instance of an overloaded class. Note that getting that property works fine.
Reproduce code:
---------------
class Object {
public $x;
function __construct($x) {
$this->x = $x;
}
}
class Overloaded {
var $props;
function __construct($x) {
$this->x = new Object($x);
}
function __get($prop) {
return $this->props[$prop];
}
function __set($prop, $val) {
$this->props[$prop] = $val;
}
}
$y = new Overloaded(2);
echo $y->x->x, " "; // Prints 2...
echo $y->x->x = 3; //Should print 3...
Expected result:
----------------
2 3
Actual result:
--------------
2
Fatal error: Cannot access undefined property for object with overloaded property access in path/to/script.php on line 22
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 20:00:01 2025 UTC |
A good solution would be to have a way of doing a "pre-set" routine i.e. __get($propertyname, $pre_set = true) and that routine is expected to return a valid object or the "Cannot access undefined..." will automatically result. Some more example code that causes the problem (no way of testing to see if the concept is correct - since this message keeps appearing): class Setting { private $mSettings; function __construct() { $constructor_args = func_get_args(); if (isset($constructor_args[0])) { $this->mSettings = array("_defaultValue"=>$constructor_args[0]); } else $this->mSettings = array("_defaultValue"=>null); } function __get($propertyName) { print "[$propertyName]<br>\n"; if ($propertyName == "_settingCount") { return count($this->mSettings) - 1; } elseif ($propertyName == "_valueNames") { //return $this->GetValueNames(); } elseif ($propertyName == "_keyNames") { //return $this->GetValueNames(); } elseif ($propertyName == "_defaultValue") { print_r($this->mSettings); return $this->mSettings["_defaultValue"]; } // It's none of the special properties. if (isset($this->mSettings[$propertyName])) { return $this->mSettings[$propertyName]; } else { // If we had the $pre_set we could do this: if ($pre_set) { // initialize for a set $this->mSettings[$propertyName] = new Setting(); return $this->mSettings[$propertyName]; } else throw new Exception("Undefined setting name."); } } function __set($propertyName, $propertyValue) { print "[$propertyName]<br>\n"; switch ($propertyName) { case "_settingCount": case "_valueNames": case "_keyNames": throw Exception("Property is read only."); break; case "_defaultValue": $this->mSettings["_defaultValue"] = $propertyValue; break; default: if (!isset($this->mSettings[$propertyName])) $this->mSettings[$propertyName] = new Setting($propertyValue); else $this->mSettings[$propertyName]->_defaultValue = $propertyValue; break; } // switch } } This code if it worked would let you do the following: $t = new Setting(); $t->includes->_defaultValue = "Automated" $t->includes->automatedIncludes->includeDirectory = "c:/includes" $t->includes->automatedIncludes = "oncePerFile" $t->includes->manualIncludes->includeDirectory = "c:/php" Which would represent: includes = Automated --> automatedIncludes = oncePerFile --> includeDirectory = c:/includes --> manualIncludes --> includeDirectory = c:/php in a lightweight manner. The layers would be created as they are needed and all with an elegant method.another "workaround", it allows to use this feature now and to convert scripts later when this feature is available in php. class a { function __call ($property, $args){ return __get($property); } use it like this: $a->b->c=10; as $a->b()->c=10; echo $a->b->c; as echo $a->b()->c;Another work around for this is: a->b->__set('c', 'value'); At least the underlying calsses do not need to be changed to make this work.Another Test Case: --------------- class TestClass { private $_p = array(); public function __get($propName){ return $this->_p[$propName]; } public function __set($propName, $propValue){ $this->_p[$propName] = $value; } } $a = new TestClass(); $a->testVar = 'test'; print $a->testVar; //--> 'test' $a->testVar = new TestClass(); $a->testVar->testVar = 'test2'; // __set of $a->testVar called instead of getter of $a->testVar and setter of $a->testVar->testVar Solution Hint: ---------------- I think I is better to call the __get method of the first objects and than call the __set method of the last one in chain. Like you with __call.I ran into this in a slightly different way -- trying to foreach through an ArrayAccess object. Interestingly, removing the __set from class O allows this to work. class O { private $m_a = array(); function __get ($key) { return $this->m_a[$key]; } function __set ($key, $val) { $this->m_a[$key] = $val; } } class A implements ArrayAccess, IteratorAggregate { private $m_e = array(); function __construct ($e = NULL) { $this->m_e = is_null ($e) ? array() : $e; } function offsetSet ($key, $value) { $this->m_e[$key] = $value; } function offsetGet ($key) { if (isset ($this->m_e[$key])) { return $this->m_e[$key]; } } function offsetUnset ($key) { unset ($this->m_e[$key]); } function offsetExists ($key) { return isset ($this->m_e[$key]); } function getIterator () { return new ArrayIterator($this->m_e); } } $o = new O(); $o->a = new A(array(1, 2, 3)); foreach ($o->a as $e) { echo "$e "; }I ran into this problem also, but slighly differently. In my case, the first property I'm accessing is a validly declared and set property of the class, instead of set using __set or read using __get. I've tried to reproduce it using simpler code, but I can't seem to get it done. The actual code I'm using is far too complex for a bug report submission (it's a customized DOM implemtation) I'll see if I can describe it at all. Basically I have the following classes that are involved: abstract class Node -- Does define __set/__get abstract class HTMLElement extends Node -- Does define __set/__get class HTMLInputElement extends HTMLElement -- Does define __set/__get class FormElement extends HTMLElement -- Does NOT define __set/__get Now, the FormElement class has variable declarations like so: private $minput; private $mlabel; private $mlplacement; private $container; $minput is eventuall an instance of HTMLInputElement when this error occurs. Here is the relevent part of the formElement->setInputType function public function setInputType($xtype){ unset($this->minput); switch ($xtype){ ...... case 'radio': case 'text': case 'entry': case 'button': case 'image': case 'submit': case 'reset': case 'checkbox': $this->minput=$this->ownerDocument->createElement('INPUT'); /* as of this point, $this->minput is a valid HTMLInputElement instance, verified w/ print_r */ $this->minput->type = $xtype; break; ... } When that method is called, I receive the error in question on the line: $this->minput->type = $xtype; I'll keep trying to come up with simpler code that reproduces the bug, but if anyone does want to view the big code (maybe for clarity) it is available at http://wiser.kicks-ass.org:8008/PHPDOM/PHPDOM-error.tar.gz My PHP version is 5.0.2, and I can reproduce with both the CLI and Apache 2 Module. Operating systems Both FreeBSD and Linux 2.6.9