Стиль неустановленных ссылок с DOM и xpath - PullRequest
2 голосов
/ 21 июня 2011

Для системы, которую я создаю, я определяю общий style, сохраненный в LINKSTYLE, который должен применяться к a элементам, которые еще не стилизованы (встроенные).Я не очень разбираюсь в DOMDocument или xpath и не могу понять, что происходит.

Благодаря Гордону я обновил свой код:

libxml_use_internal_errors(true);    

$html  = '<a href="#">test</a>'.
         '<a href="#" style="border:1px solid #000;">test2</a>';

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

foreach($xpath->query('//a[not(@style)]') as $node)
    $node->setAttribute('style','border:1px solid #000');

return $html;

С этим обновленным кодом я больше не получаю ошибок, однако элемент a не получает стиль.

Ответы [ 3 ]

2 голосов
/ 21 июня 2011

Используйте libxml_use_internal_errors(true) для подавления ошибок синтаксического анализа, возникающих из loadHTML.

Запрос XPath недействителен, поскольку contains ожидает значениеискать в атрибуте стиля.

Если вы хотите найти все якоря без элемента стиля, просто используйте

//a[not(@style)]

Вы не видите ваши изменения, потому что вы возвращаете строку, хранящуюся в $ html.После загрузки строки с помощью DOMDocument ее необходимо сериализовать обратно после того, как вы выполнили запрос и изменили внутреннее представление этой строки в DOMDocument.

Пример ( demo )

$html = <<< HTML
<ul>
    <li><a href="#foo" style="font-weight:bold">foo</a></li>
    <li><a href="#bar">bar</a></li>
    <li><a href="#baz">baz</a></li>
</ul>
HTML;
$dom = new DOMDocument;
$dom->loadHTML($html);
$xp = new DOMXpath($dom);
foreach ($xp->query('//a[not(@style)]') as $node) {
    $node->setAttribute('style', 'font-weight:bold');
}
echo $dom->saveHTML($dom->getElementsByTagName('ul')->item(0));

Вывод:

<ul>
<li><a href="#foo" style="font-weight:bold">foo</a></li>
    <li><a href="#bar" style="font-weight:bold">bar</a></li>
    <li><a href="#baz" style="font-weight:bold">baz</a></li>
</ul>

Обратите внимание, что для использования saveHTML с аргументом вам нужен как минимум PHP5.3.6.

1 голос
/ 21 июня 2011

Мне было интересно, возможно ли решить эту проблему более CCS, например, с помощью селектора.В CSS3 можно обращаться только к тем тегам <a>, которые не имеют атрибута style:

a:not([style]) {border:1px solid #000;}

Так что, если у ваших документов уже есть таблица стилей, ее можно легко добавить.

Если нет, то в документ необходимо добавить <style>.Это также можно сделать с помощью DomDocument, но я нашел это немного сложным.Однако я заставил его работать для небольшой игры:

libxml_use_internal_errors(true);    

$html  = '<a href="#">test</a>'.
         '<a href="#" style="border:1px solid #000;">test2</a>';

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

// ensure that there is a head element, body will always be there
// because of loadHtml();
$head = $dom->getElementsByTagName('head');
if (0 == $head->length) {
    $head = $dom->createElement('head');
    $body = $dom->getElementsByTagName('body')->item(0);
    $head = $body->parentNode->insertBefore($head, $body);
} else {
    $head=$head->item(0);
}

// append style tag to head.
$css = 'a:not([style]) {border:1px solid #000;}';
$style = $dom->createElement('style');
$style->nodeValue=$css;
$head->appendChild($style);

$dom->formatOutput = true;
$output = $dom->saveHtml();

echo $output;

Пример вывода:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head><style>a:not([style]) {border:1px solid #000;}</style></head>
<body>
<a href="#">test</a><a href="#" style="border:1px solid #000;">test2</a>
</body>
</html>

Если CSS конфликтует с другими, более высокими селекторами, это не простое решение.!important может помочь, хотя.

Фрагмент HTML

И что касается получения измененного фрагмента HTML, то это дополнительный код, который может работать с предложением Гордона.Просто внутренний html тега body, на этот раз я немного поиграл с SPL:

// get html fragment
$output = implode('', array_map(
  function($node) use ($dom) { return $dom->saveXml($node); },
  iterator_to_array($xpath->query('//body/*'), false)))
  ;

Foreach определенно более удобен для чтения и памяти:

// get html fragment
$output = '';
foreach($xpath->query('//body/*') as $node) 
  $output .= $dom->saveXml($node)
  ;
1 голос
/ 21 июня 2011

Первая ошибка (до редактирования) возникает, когда вы используете внутри документа & для других целей, кроме создания ссылки на сущность (например, &quot;).

Обычно это происходит в URL при разделении GET-параметров.

Вы можете игнорировать эти ошибки, используя предложение Гордона, или исправить его (заменить вхождения & на &amp;).

...