Не могу рекурсивно обойти XML-дерево

Milagro

Новичок
Не могу рекурсивно обойти XML-дерево

function hasAttribute($divnode, $count){
foreach ($divnode->child_nodes() as $chnode){
if($chnode->has_attributes())
return hasAttribute($chnode, $count+1);
else
return hasAttribute($chnode, $count);
}
return $count;
}

$xmldom = domxml_open_mem("<?xml version=\"1.0\" encoding=\"utf-8\"?><div attr=\"true\"><div attr=\"true\"></div><div><div attr=\"true\"></div></div></div>");
$divarray = $xmldom->get_elements_by_tagname("div");
$divnode = $divarray[0];

echo hasAttribute($divnode, 0);


По идее код должен подсчитать количество узлов (div-ов) у которых есть параметр. Как ни пытался, выводит все время меньше. С виду все тривиально, но есть проблема в рекурсивной функции - часть узлов теряется, не понимаю почему.


Дерево в моем примере такое:
<div attr="true">
<div attr="true"></div>
<div>
<div attr="true"></div>
</div>
</div>


Помогите, уже сломал голову!
 

olpa

Новичок
Из-за return посещаются не все дети, а только самый первый.

Перепишите функцию ьез использования count как параметра, и всё получится.
 

Vano_big

Новичок
А не проще ли было бы в данном случае использовать xPath?
В пару строк всего можно было бы обойтись.

$doc = domxml_open_file($xml_file);
$ctx = $doc->xpath_new_context();

$xpath = "count(//div[string-length(@attr) >0])";
$result = xpath_eval($ctx, $xpath);
 

Milagro

Новичок
Я упростил клд немного. На самом деле в domxml сидит все дерево, а мне надо посчитать все div с аттрибутами только в какой-то ветке domelement.

Соответственно что бы воспользоваться вашим методом, мне надо domelement со всеми потомками преобразовать в отдельных domxml-объект. Это не тривиальная задача.

-~{}~ 23.03.06 11:46:

Вот в том и вопрос, как ее переписать? Что значит "без использования счетчика"? Каким образом считать-то?

-~{}~ 23.03.06 11:52:

Счетчик вести в глобальной переменной? Из функции обращаться к ней так: $GLOBALS["count"]++; ?
 

ivankrkrkr

Новичок
Постой-ка, ты при помощи вот этой строчки:
$divarray = $xmldom->get_elements_by_tagname("div");
Выбираешь все элементы DIV из документа (ну или поддерева).
А в функции hasAttribute() ты при помощи вот этой строчки:
foreach ($divnode->child_nodes() as $chnode)
задаешь цикл, который обходит всех потомков элемента DIV. Посмотри сюда:
foreach ($divnode->child_nodes() as $chnode){
if($chnode->has_attributes())
return hasAttribute($chnode, $count+1);
else
return hasAttribute($chnode, $count);
}
Словами это выглядит так: просмотреть узлы-потомки элемента DIV и посчетать тех из них, которые имеют атрибуты.
1. Не счетаются верхние DIVы
2. Если будут элементы с именем отличным от DIV, они тоже будут посчитаны, если у них есть атрибуты.
Это совсем не сходится с твоей словестной формулировкой задачи.
раз уж ты выцепил всех потомков при помощи get_elements_by_tagname("div"), то просто пройдись по полученному массиву элементов и посмотри у кого есть атрибут.
 

Milagro

Новичок
Полностью задача такая - пройтись по всем div в документе и для каждого div сочитать количество потомков с аттрибутом.

Решается так:
PHP:
function hasAttribute($divnode){
    foreach ($divnode->child_nodes() as $chnode){
        if($chnode->has_attributes()){
	        $GLOBALS["count"]++;
	        hasAttribute($chnode);
        }
        else
            hasAttribute($chnode);
    }
    return $GLOBALS["count"];
}

$xmldom = domxml_open_mem("<?xml version=\"1.0\" encoding=\"utf-8\"?><div ><div  attr=\"true\"></div><div><div attr=\"true\"></div></div></div>");
$divarray = $xmldom->get_elements_by_tagname("div");

foreach($divarray as $divnode){
    $GLOBALS["count"] = 0;
    echo hasAttribute($divnode);
}
Вопросов больше нет, всем спасибо.
 
Сверху