PHP 5.4.16 DOMDocument removes parts of Javascript

匿名 (未验证) 提交于 2019-12-03 01:22:02

问题:

I try to load an HTML page from a remote server into a PHP script, which should manipulate the HTML with the DOMDocument class. But I have seen, that the DOMDocument class removes some parts of the Javascript, which comes with the HTML page. There are some things like:

<script type="text/javascript"> //... function printJSPage() {     var printwin=window.open('','haha','top=100,left=100,width=800,height=600');     printwin.document.writeln(' <table border="0" cellspacing="5" cellpadding="0" width="100%">');     printwin.document.writeln(' <tr>');     printwin.document.writeln(' <td align="left" valign="bottom">');     //...     printwin.document.writeln('</td>');     //... } </script> 

But the DOMDocument changes i.e. the line

printwin.document.writeln('</td>'); 

to

printwin.document.writeln(' '); 

and also a lot of others things (i.e. the last script tag is no longer there. As the result I get a complete destroyed page, which I cannot send further.

So I think, DOMDocument has problems with the HTML tags within the Javascript code and tries to correct the code, to produce a well-formed document. Can I prevent the Javascript parsing within DOMDocument?

The PHP code fragment is:

$stdin = file_get_contents('php://stdin'); $dom = new \DOMDocument(); @$dom->loadHTML($stdin); return $dom->saveHTML();   // will produce wrong HTML //return $stdin;           // will produce correct HTML 

I have stored both HTML versions and have compared both with Meld.

I also have tested

@$dom->loadXML($stdin); return $dom->saveHTML(); 

but I don't get any things back from the object.

回答1:

Here's a hack that might be helpful. The idea is to replace the script contents with a string that's guaranteed to be valid HTML and unique then replace it back.

It replaces all contents inside script tags with the MD5 of those contents and then replaces them back.

$scriptContainer = []; $str = preg_replace_callback ("#<script([^>]*)>(.*?)</script>#s", function ($matches) use (&$scriptContainer) {      $scriptContainer[md5($matches[2])] = $matches[2];         return "<script".$matches[1].">".md5($matches[2])."</script>";     }, $str); $dom = new \DOMDocument(); @$dom->loadHTML($str); $final = strtr($dom->saveHTML(), $scriptContainer);  

Here strtr is just convenient due to the way the array is formatted, using str_replace(array_keys($scriptContainer), $scriptContainer, $dom->saveHTML()) would also work.

I find it very suprising that PHP does not properly parse HTML content. It seems to instead be parsing XML content (wrongly so as well because CDATA content is parsed instead of being treated literally). However it is what it is and if you want a real document parser then you should probably look into a Node.js solution with jsdom



回答2:

If you have a <script> within a <script>, the following (not so smart) solution will handle that. There is still a problem: if the <script> tags are not balanced, the solution will not work. This could occur, if your Javascript uses String.fromCharCode to print the String </script>.

$scriptContainer = array();  function getPosition($tag) {     return $tag[0][1]; }  function getContent($tag) {     return $tag[0][0]; }  function isStart($tag) {     $x = getContent($tag);     return ($x[0].$x[1] === "<s"); }  function isEnd($tag) {     $x = getContent($tag);     return ($x[0].$x[1] === "</"); }  function mask($str, $scripts) {     global $scriptContainer;      $res = "";     $start = null;     $stop = null;     $idx = 0;      $count = 0;     foreach ($scripts as $tag) {              if (isStart($tag)) {                     $count++;                     $start = ($start === null) ? $tag : $start;             }              if (isEnd($tag)) {                     $count--;                     $stop = ($count == 0) ? $tag : $stop;             }              if ($start !== null && $stop !== null) {                     $res .= substr($str, $idx, getPosition($start) - $idx);                     $res .= getContent($start);                     $code = substr($str, getPosition($start) + strlen(getContent($start)), getPosition($stop) - getPosition($start) - strlen(getContent($start)));                     $hash = md5($code);                     $res .= $hash;                     $res .= getContent($stop);                      $scriptContainer[$hash] = $code;                      $idx = getPosition($stop) + strlen(getContent($stop));                     $start = null;                     $stop = null;             }     }      $res .= substr($str, $idx);     return $res; }  preg_match_all("#\<script[^\>]*\>|\<\/script\>#s", $html, $scripts, PREG_OFFSET_CAPTURE|PREG_SET_ORDER); $html = mask($html, $scripts);  libxml_use_internal_errors(true); $dom = new DOMDocument(); $dom->loadHTML($html); libxml_use_internal_errors(false);  // handle some things within DOM  echo strtr($dom->saveHTML(), $scriptContainer); 

If you replace the "script" String within the preg_match_all with "style" you can also mask the CSS styles, which can contain tag names too (i.e. within comments).



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!