DOMDocument appendXML со специальными символами - PullRequest
1 голос
/ 10 января 2011

Я получаю некоторые строки html из моей базы данных и хочу проанализировать эти строки в моем DOMDocument.Проблема в том, что DOMDocument выдает предупреждения в виде специальных символов.

Предупреждение: DOMDocumentFragment :: appendXML () [domdocumentfragment.appendxml]: Entity: строка 2: ошибка синтаксического анализатора: Entity 'nbsp' не определенов page.php в строке 189

Интересно, почему и мне интересно, как решить эту проблему.Это некоторые фрагменты кода моей страницы.Как я могу исправить подобные предупреждения?

$doc = new DOMDocument();

// .. create some elements first, like some divs and a h1 ..

while($row = mysql_fetch_array($result))
{
    $messageEl = $doc->createDocumentFragment();
    $messageEl->appendXML($row['message']); // gives it's warnings here!

    $otherElement->appendChild($messageEl);
}

echo $doc->saveHTML();

Я также нашел кое-что о проверке, но когда я это применил, моя страница больше не загружалась.Код, который я пробовал для этого, был примерно таким:

$implementation = new DOMImplementation();
$dtd = $implementation->createDocumentType('html','-//W3C//DTD XHTML 1.0 Transitional//EN','http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd');

$doc = $implementation->createDocument('','',$dtd);
$doc->validateOnParse = true;
$doc->formatOutput = true;

// in the same whileloop, I used the following:
$messageEl = $doc->createDocumentFragment();
$doc->validate(); // which stopped my code, but error- and warningless.
$messageEl->appendXml($row['message']);

Заранее спасибо!

Ответы [ 5 ]

6 голосов
/ 10 января 2011

В XML нет  . Единственными символьными объектами, для которых определено действительное имя (вместо использования числовой ссылки), являются &, <, >, " и '.

Это означает, что вы должны использовать числовой эквивалент неразрывного пробела, который равен   или (в шестнадцатеричном формате)  .

Если вы пытаетесь сохранить HTML в контейнер XML, сохраните его как текст. HTML и XML могут выглядеть одинаково, но они очень разные. appendXML() ожидает правильно сформированный XML в качестве аргумента. Вместо этого используйте свойство nodeValue, оно будет XML-кодировать вашу строку HTML без каких-либо предупреждений.

// document fragment is completely unnecessary
$otherElement->nodeValue = $row['message'];
5 голосов
/ 10 января 2011

Это сложный вопрос, потому что на самом деле это несколько проблем в одной.

Как указывает Томалак, в XML нет  .Таким образом, вы правильно сделали, указав DOMImplementation, потому что в XHTML есть  .Но для того, чтобы DOM знал, что документ XHTML, вы должны загрузить и проверить его на соответствие DTD.DTD находится по адресу

http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd

, но, поскольку к этой странице ежедневно поступают миллионы запросов, W3C решил заблокировать доступ к странице , если только пользовательский агент не отправлензапрос.Чтобы предоставить UserAgent, вы должны создать пользовательский контекст потока.

В коде:

// make sure DOM passes a User Agent when it fetches the DTD
libxml_set_streams_context(
    stream_context_create(
        array(
            'http' => array(
                'user_agent' => 'PHP libxml agent',
            )
        )
    )
);

// specify the implementation
$imp = new DOMImplementation;

// create a DTD (here: for XHTML)
$dtd = $imp->createDocumentType(
    'html',
    '-//W3C//DTD XHTML 1.0 Transitional//EN',
    'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
);

// then create a DOMDocument with the configured DTD
$dom = $imp->createDocument(NULL, "html", $dtd);
$dom->encoding = 'UTF-8';
$dom->validate();

$fragment = $dom->createDocumentFragment();
$fragment->appendXML('
    <head><title>XHTML test</title></head>
    <body><p>Some text with a &nbsp; entity</p></body>
    '
);
$dom->documentElement->appendChild($fragment);
$dom->formatOutput = TRUE;
echo $dom->saveXml();

Это все еще занимает некоторое время (не спрашивайте меня, почему), но в концеполучим ( переформатировать для SO )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC 
    "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>XHTML test</title>
    </head>
    <body>
        <p>Some text with a &nbsp; entity</p>
    </body>
</html>

Также см. DOMDocument :: validate () проблема

0 голосов
/ 28 мая 2019

Вот еще один подход, потому что мы не хотели, возможно, медленных сетевых запросов (или вообще никаких сетевых запросов в результате ввода данных пользователем):

<?php
$document = new \DOMDocument();
$document->loadHTML('<html><body></body></html>');

$html = '<b>test&nbsp;</b>';
$fragment = $document->createDocumentFragment();

$html = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE document [
<!ENTITY nbsp   "&#160;" >
]>
<document>'.$html.'</document>';

$newdom = new \DOMDocument();
$newdom->loadXML($html, LIBXML_HTML_NOIMPLIED | LIBXML_NOCDATA | LIBXML_NOENT | LIBXML_NONET | LIBXML_NOBLANKS);

foreach ($newdom->documentElement->childNodes as $childnode)
  $fragment->appendChild($fragment->ownerDocument->importNode($childnode, TRUE));

$document->getElementsByTagName('body')[0]->appendChild($fragment);

echo $document->saveHTML();

Здесь мы включаем соответствующую часть DTD, в частности определения latin1 как внутреннее определение DOCTYPE.Затем содержимое HTML помещается в элемент документа, чтобы иметь возможность обрабатывать последовательность дочерних элементов.Затем проанализированные узлы импортируются и добавляются в целевой DOM.

Наша фактическая реализация использует file_get_contents для загрузки DTD, содержащего все определения сущностей, из локального файла.

0 голосов
/ 05 декабря 2011

Хотя smarty может быть хорошей ставкой (зачем изобретать колесо в 14-й раз?), У etranger может быть смысл.Существуют ситуации, в которых вы не хотите использовать что-то излишнее, например, совершенно новый (и не изученный) пакет, но больше похоже на то, что вы хотите опубликовать некоторые данные из базы данных, которая, как оказалось, содержит html-материал, с которым у парсера XML есть проблемы.

Внимание, ниже приведено простое решение, но не делайте этого, если вы НЕ УВЕРЕНЫ, что вам это сойдет с рук!(Я сделал это, когда у меня было около 2 часов до крайнего срока, и у меня не было времени на учебу, оставил один только реализовать что-то вроде smarty ...)

Прежде чем вставлять строку в функцию appendXML, выполните ее черезpreg_replace.Например, замените все & nbsp;символы с [some_prefix] _nbsp.Затем, на странице, где вы показываете HTML, сделайте это наоборот.

И Престо!=)

Пример кода: Код, который помещает текст во фрагмент документа:

// add text tag to p tag.
// print("CCMSSelTextBody::getDOMObject: strText: ".$this->m_strText."<br>\n");
$this->m_strText = preg_replace("/&nbsp;/", "__nbsp__", $this->m_strText);
$domTextFragment = $domDoc->createDocumentFragment();
$domTextFragment->appendXML(utf8_encode($this->m_strText));
$p->appendChild($domTextFragment);
// $p->appendChild(new DOMText(utf8_encode($this->m_strText)));

Код, который проанализировал строку и записал html:

// Instantiate template.
$pTemplate = new CTemplate($env, $pageID, $pUser, $strState);

// Parse tag-sets.
$pTemplate->parseTXTTags();
$pTemplate->parseCMSTags();

// present the html code.
$html = $pTemplate->getPageHTML();
$html = preg_replace("/__nbsp__/", "&nbsp;", $html);
print($html);

Это, вероятно,хорошая идея придумать более сильную замену.(Если вы настаиваете на тщательности: выполните md5 для значения time () и жестко закодируйте результат этого в качестве префикса. Как в первом фрагменте:

$this->m_strText = preg_replace("/&nbsp;/", "4597ee308cd90d78aa4655e76bf46ee0_nbsp", $this->m_strText);

И во втором:

$html = preg_replace("/4597ee308cd90d78aa4655e76bf46ee0_nbsp/", "&nbsp;", $html);

Сделайте то же самое с любыми другими тегами и вещами, которые вам нужно обойти.

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

Используйте вышеперечисленное на свой страх и риск.

0 голосов
/ 10 января 2011

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

Может быть так, что ваша задача требует включения тегированных данных из базы данных в итоговый XML, но может требовать или не требовать синтаксического анализа. Если это просто данные для включения, а не структурированные части вашего XML, вы можете поместить строки из базы данных в разделы CDATA , эффективно обходя все ошибки проверки на этом этапе.

...