2016-02-26 11 views
6

registerNodeClassという新しいベースノードタイプを登録すると、作成した要素に変数名を再利用すると、カスタムプロパティがデフォルト値に戻ります。私は実際にループでこれをやろうとしているが、ここで私が何を意味するかを明確に示し思い例です:PHP registerNodeClassと再利用変数名

<?php 

class myDOMElement extends DOMElement 
{ 
    public $myProp = 'Some default'; 
} 

$doc = new DOMDocument(); 
$doc->registerNodeClass('DOMElement', 'myDOMElement'); 

$node = $doc->createElement('a'); 
$node->myProp = 'A'; 
$doc->appendChild($node); 

# This seems to alter node A in $doc, not what I expected: 
$node = $doc->createElement('b'); 
$node->myProp = 'B'; 
$doc->appendChild($node); 

# Note: $nodeC instead of $node, this works fine. 
$nodeC = $doc->createElement('c'); 
$nodeC->myProp = 'C'; 
$doc->appendChild($nodeC); 

foreach ($doc->childNodes as $n) { 
    echo 'Tag ', $n->tagName, ' myProp:', PHP_EOL; 
    var_dump($n->myProp); 
} 

は、なぜ私は、タグa代わりの値"A"ため"Some default"を得るのですか?

Tag a myProp: 
string(12) "Some default" 
Tag b myProp: 
string(1) "B" 
Tag c myProp: 
string(1) "C" 
+0

:http://stackoverflow.com/q/5473967ため –

答えて

2

私たちは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イテレータオブジェクトを作成します。現在のイテレータ値is fetchedzvalからphp_dom_iterator_move_forwardで作成されています。後者は内部XML構造に基づいて"creates new object"(特にDOMNode) のみです。

しかし、方法はphp_dom_create_objectオブジェクトを作成するトリッキーです!

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; 
} 

フリーオブジェクトハンドラで(と呼ばれるオブジェクトが最初に構築される場合、それはphp_libxml_increment_node_ptrによってポインタを保存しますオブジェクトが破棄されている)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のプロパティも破壊されています。

foreach ($doc->childNodes as $n) { 
    var_dump($n->tagName); 
} 

したがって、あなたの質問

への答えは、なぜ私は「得るか:しかし、DOM拡張は、我々はループの中でそれを要求した場合、aノードに対して新しいインスタンスを生成します。値 "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" 
+0

ファンタスティック答え、おかげで非常にここで

は、回避策ですすべての詳細。この問題のPHPバグを作成しました:https://bugs.php.net/bug.php?id=71872 –

関連する問題