Если я попытаюсь загрузить HTML-документ в PHP DOM, я получаю сообщение об ошибке:
Error DOMDocument::loadHTML() [domdocument.loadhtml]: ID someAnchor already defined in Entity, line: 9
Я не могу понять, почему. Вот код, который загружает HTML-строку в DOM.
Во-первых, без анкерного тега, а второй с одним. Во втором документе возникает ошибка.
Надеюсь, вы сможете разрезать и вставить его в скрипт и запустить его, чтобы увидеть тот же результат:
<?php ini_set('display_errors', 1); error_reporting(E_ALL); $stringWithNoAnchor = <<<EOT <!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> <title>My document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body > <h1>Hello</h1> </body> </html> EOT; $stringWithAnchor = <<<EOT <!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> <title>My document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body > <h1>Hello</h1> <a name="someAnchor" id="someAnchor"></a> </body> </html> EOT; class domGrabber { public $_FileErrorStr = ''; /** *@desc DOM object factory does the work of loading the DOM object */ public function getLoadAsDOMObj($htmlString) { $this->_FileErrorStr =''; //reset error container $xmlDoc = new DOMDocument(); set_error_handler(array($this, '_FileErrorHandler')); // Warnings and errors are suppressed $xmlDoc->loadHTML($htmlString); restore_error_handler(); return $xmlDoc; } /** *@desc public so that it can catch errors from outside this class */ public function _FileErrorHandler($errno, $errstr, $errfile, $errline) { if ($this->_FileErrorStr === null) { $this->_FileErrorStr = $errstr; } else { $this->_FileErrorStr .= (PHP_EOL . $errstr); } } } $domGrabber = new domGrabber(); $xmlDoc = $domGrabber->getLoadAsDOMObj($stringWithNoAnchor ); echo 'PHP Version: '. phpversion() .'<br />'."\n"; echo '<pre>'; print $xmlDoc->saveXML(); echo '</pre>'."\n"; if ($domGrabber->_FileErrorStr) { echo 'Error'. $domGrabber->_FileErrorStr; } $xmlDoc = $domGrabber->getLoadAsDOMObj($stringWithAnchor); echo '<pre>'; print $xmlDoc->saveXML(); echo '</pre>'."\n"; if ($domGrabber->_FileErrorStr) { echo 'Error'. $domGrabber->_FileErrorStr; }
Я получаю следующее из моего исходного кода в Firefox:
PHP Version: 5.2.9<br /> <pre><?xml version="1.0" encoding="iso-8859-1" standalone="yes"?> <!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" xmlns="http://www.w3.org/1999/xhtml"><head><title>My document</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /></head><body> <h1>Hello</h1> </body></html> </pre> <pre><?xml version="1.0" encoding="iso-8859-1" standalone="yes"?> <!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" xmlns="http://www.w3.org/1999/xhtml"><head><title>My document</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /></head><body> <h1>Hello</h1> <a name="someAnchor" id="someAnchor"></a> </body></html> </pre> Error DOMDocument::loadHTML() [<a href='domdocument.loadhtml'>domdocument.loadhtml</a>]: ID someAnchor already defined in Entity, line: 9
Итак, почему DOM говорит, что someAnchor уже определен?
Обновить:
Я экспериментировал с обоими
См. Сценарий сравнения здесь для завершения:
<?php ini_set('display_errors', 1); error_reporting(E_ALL); $stringWithNoAnchor = <<<EOT <!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> <title>My document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body > <p>stringWithNoAnchor</p> </body> </html> EOT; $stringWithAnchor = <<<EOT <!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> <title>My document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body > <p>stringWithAnchor</p> <a name="someAnchor" id="someAnchor" ></a> </body> </html> EOT; $stringWithAnchorButOnlyIdAtt = <<<EOT <!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> <title>My document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body > <p>stringWithAnchorButOnlyIdAtt</p> <a id="someAnchor"></a> </body> </html> EOT; class domGrabber { public $_FileErrorStr = ''; public $useHTMLMethod = TRUE; /** *@desc DOM object factory does the work of loading the DOM object */ public function loadDOMObjAndWriteOut($htmlString) { $this->_FileErrorStr =''; $xmlDoc = new DOMDocument(); set_error_handler(array($this, '_FileErrorHandler')); // Warnings and errors are suppressed if ($this->useHTMLMethod) { $xmlDoc->loadHTML($htmlString); } else { $xmlDoc->loadXML($htmlString); } restore_error_handler(); echo "<h1>"; echo ($this->useHTMLMethod) ? 'using xmlDoc->loadHTML() ' : 'using $xmlDoc->loadXML()'; echo "</h1>"; echo '<pre>'; print $xmlDoc->saveXML(); echo '</pre>'."\n"; if ($this->_FileErrorStr) { echo 'Error'. $this->_FileErrorStr; } } /** *@desc public so that it can catch errors from outside this class */ public function _FileErrorHandler($errno, $errstr, $errfile, $errline) { if ($this->_FileErrorStr === null) { $this->_FileErrorStr = $errstr; } else { $this->_FileErrorStr .= (PHP_EOL . $errstr); } } } $domGrabber = new domGrabber(); echo 'PHP Version: '. phpversion() .'<br />'."\n"; $domGrabber->useHTMLMethod = TRUE; //DOM->loadHTML $domGrabber->loadDOMObjAndWriteOut($stringWithNoAnchor); $domGrabber->loadDOMObjAndWriteOut($stringWithAnchor ); $domGrabber->loadDOMObjAndWriteOut($stringWithAnchorButOnlyIdAtt); $domGrabber->useHTMLMethod = FALSE; //use DOM->loadXML $domGrabber->loadDOMObjAndWriteOut($stringWithNoAnchor); $domGrabber->loadDOMObjAndWriteOut($stringWithAnchor ); $domGrabber->loadDOMObjAndWriteOut($stringWithAnchorButOnlyIdAtt);
Если вы загружаете файлы XML (это так, XHTML – это XML), то вы должны использовать DOMDocument::loadXML()
, а не DOMDocument::loadHTML()
.
В HTML оба name
и id
вводят идентификатор. Таким образом, вы повторяете идентификатор «someAnchor», следовательно, ошибку.
Однако валидатор W3C позволяет повторять идентификаторы в форме, которую вы показываете <a id="someAnchor" name="someAnchor"></a>
. Это может быть ошибка libmxl2.
В этом отчете об ошибке для libxml2 пользователь предлагает патч, чтобы рассматривать атрибут name
как идентификатор:
Согласно спецификациям HTML и XHTML, только атрибут имени элемента разделяет пространство имен с атрибутами id. Для некоторых элементов можно утверждать, что несколько экземпляров с тем же именем не имеют смысла, но они, тем не менее, не должны рассматриваться в том же пространстве имен, что и атрибуты id других элементов.
См. http://www.zvon.org/xxl/xhtmlReference/Output/Strict/attr_name.html для всех элементов, которые принимают атрибуты имени и их семантику.