Reputation: 1179
See this example:
This class extends DOMElement. Every DOMElement must be a part of any DOMDocuement to be modifable.
class Xmlrize extends DOMElement
{
private static $dom;
public function __construct(/*. string .*/ $name,$value =null) {
parent::__construct($name,$value,null);
if(!self::$dom) {
self::$dom = new DOMDocument('1.0','UTF-8');
}
self::$dom->appendChild($this);
}
}
Used to add array into DOM. If value of the array is not Xmlrize, then I ignore it.
class Attributes extends Xmlrize {
public function __construct(array $attributes)
{
parent::__construct('Attributes');
foreach($attributes as $name => $value) {
$element = new Xmlrize($name);
if($value instanceof Xmlrize)
$element->appendChild($value);
$this->appendChild($element);
}
}
}
You can add instance of Attributes in the constructor
class Html extends Xmlrize {
public function __construct(Attributes $obj) {
parent::__construct('html');
$this->addAttributes($obj);
}
This method shows if attribute is or is not instance of Xmlrize.
protected function addAttributes(Attributes $attributes)
{
foreach($attributes->childNodes as $attribut) {
if($attribut->childNodes->length == 1) {
$item = $attribut->childNodes->item(0);
echo $attribut->nodeName.': ';
echo $item->nodeName.' has value '.$item->nodeValue.'. ';
echo 'Is it DomElement? ';
echo ($item instanceof DomElement) ? 'true' : 'false';
echo '. Is it Xmlrize? ';
echo ($item instanceof Xmlrize) ? 'true' : 'false';
echo '.'.PHP_EOL;
}
}
return null;
}
}
Example of initiating of Attributes by array. See the differences!
/* works as expected */
$like = new Html(new Attributes(
$xx = array('XXX'=> new Xmlrize('foo', 'baar'))
));
/* do not work as exptected */
$like = new Html(new Attributes(
array('XXX'=> new Xmlrize('foo', 'baar'))
));
?>
The example above returns:
XXX: foo has value baar. Is it DomElement? true. Is it Xmlrize? true.
XXX: foo has value baar. Is it DomElement? true. Is it Xmlrize? false.
Why if I add '$xx =' the element is considered as Xmlrize and if not, it isn't?
Upvotes: 1
Views: 241
Reputation: 7389
Congratulations, you have found a bug in the PHP's implementation of DOM. I have spent some time with your code and I was able to narrow down the buggy behaviour to this simple test-case:
class MyElement extends DOMElement { }
// #1 - okay
$dom = new DOMDocument();
$e = new MyElement("e");
$dom->appendChild($e);
echo get_class($dom->childNodes->item(0)) . "\n";
// #2 - wrong
$dom = new DOMDocument();
$dom->appendChild(new MyElement("e"));
echo get_class($dom->childNodes->item(0)) . "\n";
// #3 - wrong
$dom = new DOMDocument();
$e = new MyElement("e");
$dom->appendChild($e);
$e = null;
echo get_class($dom->childNodes->item(0)) . "\n";
This is obviously a bug in PHP, but here is why is this happening. Every object in PHP has a reference count (how many references to this object are there anywhere). When you call $dom->appendChild(...)
, this function should internally increase the object's refernce count, but it doesn't. When you run sample #1, all seems fine, but in sample #2, the object is created "temporarily" (only for the call), with refcount of 1. When appendChild
is done, PHP reduced the refcount to 0 and therefore deletes the object from memory.
Now comes the tricky part: You should now think "wow, that means that there is some pointer to a deallocated (dead) memory and the program should crash badly". But it doesn't simply because PHP's memory manager doesn't really deallocate unused memory, it simply marks it as deallocated. So you can still e.g. read from it, although the contents of the memory may be already altered by another allocation. And that's what really happens. I checked with the debugger that the object gets really deallocated (and it obviously shouldn't, it should have a reference from the DOM hierarchy).
Now what you should do is 1) submit a bug report to PHP's website and 2) either stop using DOMElement class or make sure that every object that you "append" to the hierarchy of DOM is references somewhere else, too.
Also, if it's not yet clear, here is why you experienced a different behaviour between foo(array(...))
and foo($x = array(...))
: The second one assigns the array to a global (or local, depends where you call it) variable, that will hold the array and in the array, there is a reference to your object. Thus is won't be deallocated, and the hierarchy in DOM is okay.
Let me know if your going to submit the bug report or not. I will do it instead if you want to, but hey, it's your bug :)
Upvotes: 1
Reputation:
Only difference between foo(array(…))
and foo($x=array(…))
is:
foo(array(…))
only passes array as argument to foo method.
foo($x=array(…))
saves array to $x variable and passes the same variable $x to foo method.
Upvotes: 0