我们假设我们使用PHP7(所描述的行为至少为PHP版本5..7所特有)。
的DOMNode::appendChild
方法,树立新DOMNode
对象的内部结构,更新父节点的内部结构(在我们的情况下,它是一个DOMDocument
对象),然后创建并返回根据建立的内部结构新DOMNode
对象。事实上返回的对象和附加的子节点的对象是相同的:
$ret_node = $doc->appendChild($node);
debug_zval_dump($node);
debug_zval_dump($ret_node);
var_dump(spl_object_hash($node));
var_dump(spl_object_hash($ret_node));
输出:
object(myDOMElement)#2 (18) refcount(3){
..
object(myDOMElement)#2 (18) refcount(3){
...
string(32) "00000000121277ac00000000658254f1"
string(32) "00000000121277ac00000000658254f1"
的DOMNode::$childNodes
属性读取处理器创建DOMNodeList
iterator对象。当前迭代器值is fetched来自zval
,由php_dom_iterator_move_forward
准备。后者仅基于内部XML结构的"creates new object"(特别是,DOMNode
) 。
但方式php_dom_create_object
创建的对象是棘手的!如果对象被构造第一时间,这样可以节省的php_libxml_increment_node_ptr
指针借助于:
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, obj, (void *)intern);
下一次它调用php_dom_create_object
它detects the saved pointer, increments reference count, and returns the previously created object:
if ((intern = (dom_object *) php_dom_object_get_data((void *) obj))) {
GC_REFCOUNT(&intern->std)++;
ZVAL_OBJ(return_value, &intern->std);
return 1;
}
在自由对象处理程序(这被称为时该对象正在被销毁)DOM扩展callsphp_libxml_decrement_node_ptr
。
正如我们所见,DOM对象实际上只要有任何PHP变量就会离开。如果变量超出范围,则会被销毁。在这种情况下,DOM扩展将为我们生成一个新对象。
现在让我们析构函数添加到myDOMElement
类:
class myDOMElement extends DOMElement
{
public $myProp = 'Some default';
public function __destruct() {
echo __METHOD__, PHP_EOL;
}
}
然后将下面的代码将显示该DOMNode
对象在这里我们给$doc->createElement('b')
它的线被破坏:
$node = $doc->createElement('a');
$node->myProp = 'A';
$doc->appendChild($node);
echo "Marker B-1\n";
$node = $doc->createElement('b');
echo "Marker B-2\n";
$node->myProp = 'B';
$doc->appendChild($node);
输出:
Marker B-1
myDOMElement::__destruct
Marker B-2
由于DOM扩展本身不存储zval
对象,因此存储在$node
变量中的上一个对象将超出范围并自动销毁。从现在开始,我们没有对PHP对象的引用。它的myProp
属性也被摧毁。然而,DOM扩展将产生新实例为a
节点,如果我们在一个循环中提出要求:
foreach ($doc->childNodes as $n) {
var_dump($n->tagName);
}
因此,回答你的问题
为什么我得到“一些默认的“标签a而不是值”A“?
是:用$myProp = "A"
的对象实际上是销毁,因为它超出范围时,再指定对象的$node
变量,并为我们的DOM扩展不存储PHP对象 - 它代表这个责任用户。但是,该节点仍然存在于内部DOM结构中。因此,当涉及到循环中的A
标记时,DOM扩展会生成具有默认属性的新对象。
这里是一个解决办法:
foreach (['a', 'b'] as $name) {
$nodes[] = $node = $doc->createElement($name);
$node->myProp = $name;
$doc->appendChild($node);
}
foreach ($doc->childNodes as $n) {
echo 'Tag ', $n->tagName, ' myProp:'; var_dump($n->myProp);
}
unset($nodes);
输出
Tag a myProp:string(1) "a"
Tag b myProp:string(1) "b"
这个问题可能涉及:http://stackoverflow.com/q/5473967 –