дом — Php, DOMXpath. Неверный товар (x) возврат

Простая вещь, но … У нас есть такой код PHP

$oPath = new \DOMXPath($this->oHtmlProperty);
$oNode = $oPath->query('//div[@class="product-spec__body"]');

foreach ($oNode as $oNodeProperty) {
$oListTitle = $oPath->query('h2[@class="title title_size_22"]', $oNodeProperty);

// ### VARIANT 1 (error with message 'Trying to get property of non-object')

// $aPropertyGroup = [
//     'title' => $oListTitle->item(0)->textContent,
//     'property' => []
// ];

// ### VARIANT 2

foreach ($oListTitle as $oListTitleItem){
$aPropertyGroup = [
'title' => $oListTitleItem->textContent,
'property' => []
];

break; // we need only first item
}

// ....

Так что главное $oListTitle всегда ->item(0) узел и не более. Когда мы пытаемся получить это, мы получаем ошибку with message 'Trying to get property of non-object' но этот узел существует! Когда мы делаем то же самое, но через итерацию (возвращаем тот же класс узла, который мы вызываем -> item (x)), мы получаем то, что нам нужно.

Может кто-нибудь сказать, почему? XD

ДОБАВЛЕНО:

$ oListTitle — это:

object(DOMNodeList)#340 (1) { ["length"]=> int(1) }

ДОБАВЛЕНО:

var_dump($oListTitle->item(0)); верни этот

object(DOMElement)#338 (18) { ["tagName"]=> string(2) "h2" ["schemaTypeInfo"]=> NULL ["nodeName"]=> string(2) "h2" ["nodeValue"]=> string(45) "ОÑновные характериÑтики" ["nodeType"]=> int(1) ["parentNode"]=> string(22) "(object value omitted)" ["childNodes"]=> string(22) "(object value omitted)" ["firstChild"]=> string(22) "(object value omitted)" ["lastChild"]=> string(22) "(object value omitted)" ["previousSibling"]=> NULL ["nextSibling"]=> string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" ["ownerDocument"]=> string(22) "(object value omitted)" ["namespaceURI"]=> NULL ["prefix"]=> string(0) "" ["localName"]=> string(2) "h2" ["baseURI"]=> NULL ["textContent"]=> string(45) "ОÑновные характериÑтики" }

Еще одно слово не пустое и существует.

0

Решение

Я не могу воспроизвести проблему, используя php 5.6.3 / win32 и следующий код (ваш код + образец)

<?php
$foo = new Foo;
var_export($foo->bar());

class Foo {

public function __construct() {
$this->oHtmlProperty = new DOMDocument;
$this->oHtmlProperty->loadhtml('<html><head><title>...</title></head><body>
<div class="product-spec__body">
<h2 class="title title_size_22">h2_1</h2>
<h2 class="title title_size_22">h2_2</h2>
</div>
<div></div>
<div class="product-spec__body">
<h2 class="title title_size_22">h2_3</h2>
<h2 class="title title_size_22">h2_4</h2>
</div>
</body></html>');
}

public function bar() {
$retval = array(); $aPropertyGroup = array();
$oPath = new \DOMXPath($this->oHtmlProperty);
$oNode = $oPath->query('//div[@class="product-spec__body"]');

foreach ($oNode as $oNodeProperty) {
$oListTitle = $oPath->query('h2[@class="title title_size_22"]', $oNodeProperty);
// ### VARIANT 1 (error with message 'Trying to get property of non-object')
if ( !is_object($oListTitle) ) die('$oListTitle is not an object');
if ( ! ($oListTitle instanceof DOMNodeList) ) die('$oListTitle is not a DOMNodeList');
if ( $oListTitle->length < 1 ) die('oListTitle->length < 1');
$node = $oListTitle->item(0);
if ( is_null($node) ) die('$node is NULL');
if ( !is_object($node) ) die('$node is not an object');
if ( ! ($node instanceof DOMNode) ) die('$node is not a DOMNode');

$aPropertyGroup = [
'title' => $oListTitle->item(0)->textContent,
'property' => []
];

if ( !empty($aPropertyGroup) ) {
$retval[] = $aPropertyGroup;
$aPropertyGroup = array();
}
}

return $retval;
}
}

выход

array (
0 =>
array (
'title' => 'h2_1',
'property' =>
array (
),
),
1 =>
array (
'title' => 'h2_3',
'property' =>
array (
),
),
)

как и ожидалось.
Но возможно libxml_get_last_error () могу рассказать тебе больше ….

1

Другие решения

У вас есть два выражения, поэтому, если первое совпадение имеет несколько элементов. Возможно, что внутреннее совпадение имеет разные результаты в зависимости от внешнего совпадения. Вы устанавливаете только одну переменную, поэтому, если желаемый результат находится в одном из внешних совпадений, он заполнит переменную.

Вы не предоставили HTML, поэтому невозможно воспроизвести ошибку.

Но если вы используете DOMNodelist::item() Вы всегда должны проверять, что возвращаемое значение является узлом.

Вот две возможные оптимизации:

  1. Ограничьте результат первым узлом:
    h2[@class="title title_size_22"][1]
  2. Получить текстовое содержимое первого узла как строку (работает только с DOMXPath::evaluate()):
    string(h2[@class="title title_size_22"])

пример

$html = <<<'HTML'
<html><head><title>...</title></head><body>
<div class="product-spec__body">
<h2 class="title title_size_22">h2_1</h2>
<h2 class="title title_size_22">h2_2</h2>
</div>
<div></div>
<div class="product-spec__body">
</div>
</body></html>
HTML;

$dom = new DOMDocument();
$dom->loadHtml($html);
$xpath = new DOMXpath($dom);

foreach ($xpath->evaluate('//div[@class="product-spec__body"]') as $index => $spec) {
echo "Run #", $index, "\n";
// all h2 with the class
var_dump($xpath->evaluate('h2[@class="title title_size_22"]', $spec));
// first h2 with the class
var_dump($xpath->evaluate('h2[@class="title title_size_22"][1]', $spec));
// first h2 with the class as string
var_dump($xpath->evaluate('string(h2[@class="title title_size_22"])', $spec));
echo "\n\n";
}

Выход — сравните результаты двух прогонов:

Run #0
object(DOMNodeList)#9 (1) {
["length"]=>
int(2)
}
object(DOMNodeList)#8 (1) {
["length"]=>
int(1)
}
string(4) "h2_1"

Run #1
object(DOMNodeList)#8 (1) {
["length"]=>
int(0)
}
object(DOMNodeList)#8 (1) {
["length"]=>
int(0)
}
string(0) ""
1

По вопросам рекламы [email protected]