[php] HTML 래퍼없이 DOMDocument의 HTML을 저장하는 방법은 무엇입니까?

나는 아래의 기능 이며, 콘텐츠 출력 전에 XML, HTML, bodyp 태그 래퍼를 추가하지 않고 DOMDocument를 출력하기 위해 고군분투 하고 있습니다. 제안 된 수정 사항 :

$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));

콘텐츠에 블록 수준 요소가없는 경우에만 작동합니다. 그러나 h1 요소가있는 아래 예에서와 같이이 경우 saveXML의 결과 출력이 다음으로 잘립니다.

<p> 원하는 경우 </ p>

가능한 해결 방법으로이 게시물을 언급했지만이 솔루션에 구현하는 방법을 이해할 수 없습니다 (아래 주석 처리 된 시도 참조).

어떤 제안?

function rseo_decorate_keyword($postarray) {
    global $post;
    $keyword = "Jasmine Tea"
    $content = "If you like <h1>jasmine tea</h1> you will really like it with Jasmine Tea flavors. This is the last ocurrence of the phrase jasmine tea within the content. If there are other instances of the keyword jasmine tea within the text what happens to jasmine tea."
    $d = new DOMDocument();
    @$d->loadHTML($content);
    $x = new DOMXpath($d);
    $count = $x->evaluate("count(//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and (ancestor::b or ancestor::strong)])");
    if ($count > 0) return $postarray;
    $nodes = $x->query("//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and not(ancestor::h1) and not(ancestor::h2) and not(ancestor::h3) and not(ancestor::h4) and not(ancestor::h5) and not(ancestor::h6) and not(ancestor::b) and not(ancestor::strong)]");
    if ($nodes && $nodes->length) {
        $node = $nodes->item(0);
        // Split just before the keyword
        $keynode = $node->splitText(strpos($node->textContent, $keyword));
        // Split after the keyword
        $node->nextSibling->splitText(strlen($keyword));
        // Replace keyword with <b>keyword</b>
        $replacement = $d->createElement('strong', $keynode->textContent);
        $keynode->parentNode->replaceChild($replacement, $keynode);
    }
$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));
//  $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->item(1));
//  $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->childNodes);
return $postarray;
}



답변

PHP 5.4 및 Libxml 2.6 부터는 이제 콘텐츠를 구문 분석하는 방법에 대해 Libxml에 지시 하는 매개 변수 가 있기 때문에이 모든 답변은 이제 잘못 되었습니다 .loadHTML$option

따라서 이러한 옵션을 사용하여 HTML을로드하면

$html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

할 때 saveHTML()no doctype, no <html>, no <body>.

LIBXML_HTML_NOIMPLIED암시 적 html / body 요소의 자동 추가를 해제하면
LIBXML_HTML_NODEFDTD기본 doctype이 발견되지 않을 때 추가되는 것을 방지합니다.

Libxml 매개 변수에 대한 전체 문서는 여기에 있습니다.

( loadHTML문서에 따르면 Libxml 2.6이 필요하지만 LIBXML_HTML_NODEFDTDLibxml 2.7.8에서만 사용할 LIBXML_HTML_NOIMPLIED수 있으며 Libxml 2.7.7에서 사용할 수 있습니다.)


답변

loadHTML ()로 문서를로드 한 후 바로 노드를 제거하면됩니다.

# remove <!DOCTYPE 
$doc->removeChild($doc->doctype);

# remove <html><body></body></html> 
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);


답변

saveXML()대신 사용 하고 documentElement를 인수로 전달하십시오.

$innerHTML = '';
foreach ($document->getElementsByTagName('p')->item(0)->childNodes as $child) {
    $innerHTML .= $document->saveXML($child);
}
echo $innerHTML;

http://php.net/domdocument.savexml


답변

최상위 답변의 문제는 그것이 LIBXML_HTML_NOIMPLIED불안정 하다는 것 입니다 .

요소의 순서를 변경할 수 있습니다 (특히 맨 위 요소의 닫는 태그를 문서 맨 아래로 이동), 임의의 p태그 추가 및 기타 다양한 문제 [1] . htmlbody태그를 제거 할 수 있지만 불안정한 동작이 발생합니다. 프로덕션에서는 이는 위험 신호입니다. 요컨대 :

사용하지 마십시오LIBXML_HTML_NOIMPLIED . 대신substr .


생각해보십시오. <html><body>및 의 길이는 </body></html>문서의 양쪽 끝에 고정되어 있습니다. 크기는 변경되지 않으며 위치도 변경되지 않습니다. 이것은 우리가 substr그들을 잘라내 는 데 사용할 수 있습니다.

$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);

echo substr($dom->saveHTML(), 12, -15); // the star of this operation

( 이것은 최종 솔루션이 아닙니다! 전체 답변은 아래를 참조 하고 컨텍스트를 계속 읽으십시오)

우리는 잘라 12때문에 문서의 선두에서 떨어져 <html><body>= 12 자 ( <<>>+html+body= 4 + 4 + 4), 우리는 뒤로 가서 컷 (15) 마지막을 지나고 있기 때문에 \n</body></html>(= 15 자\n+//+<<>>+body+html = 1 + 2 + 4 + 4 + 4)

나는 여전히 포함되는 것을 LIBXML_HTML_NODEFDTD생략합니다 !DOCTYPE. 첫째, 이것은 substrHTML / BODY 태그 의 제거를 단순화합니다 . 둘째, substrdefault doctype‘가 항상 고정 된 길이 인지 알 수 없기 때문에 doctype을 제거 하지 않습니다 . 그러나 가장 중요한 것은 LIBXML_HTML_NODEFDTDDOM 파서가 HTML5가 아닌 문서 유형을 문서에 적용하지 못하도록하는 것입니다. 이는 최소한 파서가 느슨한 텍스트로 인식하지 않는 요소를 처리하는 것을 방지합니다.

우리는 HTML / BODY 태그는 고정 길이와 위치로되어 있다는 사실을 알고, 우리는 상수가 좋아 것을 알고 LIBXML_HTML_NODEFDTD위의 방법은 미래에 잘 굴러해야하므로, 사용 중단 통지의 몇 가지 유형없이 제거되지 않습니다, 하지만


… 유일한주의 사항은 DOM 구현 HTML / BODY 태그가 문서 내에 배치되는 방식을 변경할 수 있다는 것입니다. 예를 들어 문서 끝에서 줄 바꿈을 제거하거나 태그 사이에 공백을 추가하거나 줄 바꿈을 추가 할 수 있습니다.

이 문제는에 대한 여는 태그와 닫는 태그의 위치를 ​​검색 body하고 잘라낼 길이에 대한 오프셋을 사용하여 해결할 수 있습니다 . 및를 사용 strpos하여 strrpos각각 앞과 뒤에서 오프셋을 찾습니다.

$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);

$trim_off_front = strpos($dom->saveHTML(),'<body>') + 6;
// PositionOf<body> + 6 = Cutoff offset after '<body>'
// 6 = Length of '<body>'

$trim_off_end = (strrpos($dom->saveHTML(),'</body>')) - strlen($dom->saveHTML());
// ^ PositionOf</body> - LengthOfDocument = Relative-negative cutoff offset before '</body>'

echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end);

마지막으로 미래를 보장하는 최종 답변을 반복합니다 .

$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);

$trim_off_front = strpos($dom->saveHTML(),'<body>') + 6;
$trim_off_end = (strrpos($dom->saveHTML(),'</body>')) - strlen($dom->saveHTML());

echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end);

doctype, html 태그, body 태그가 없습니다. DOM 파서가 곧 새로운 페인트 칠을 받기를 바라며 원하지 않는 태그를보다 직접적으로 제거 할 수 있습니다.


답변

깔끔한 트릭이 사용하는 loadXML다음과 saveHTML. htmlbody태그가 삽입되는 load단계가 아닌 save단계.

$dom = new DOMDocument;
$dom->loadXML('<p>My DOMDocument contents are here</p>');
echo $dom->saveHTML();

이것은 약간 엉망이며 작동하도록 할 수 있다면 Jonah의 대답을 사용해야합니다.


답변

DOMDocumentFragment 사용

$html = 'what you want';
$doc = new DomDocument();
$fragment = $doc->createDocumentFragment();
$fragment->appendXML($html);
$doc->appendChild($fragment);
echo $doc->saveHTML();


답변

2017 년이고 2011 년 질문에 대한 답이 마음에 들지 않습니다. 많은 정규식, 큰 클래스, loadXML 등 …

알려진 문제를 해결하는 쉬운 솔루션 :

$dom = new DOMDocument();
$dom->loadHTML( '<html><body>'.mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8').'</body></html>' , LIBXML_HTML_NODEFDTD);
$html = substr(trim($dom->saveHTML()),12,-14);

쉽고, 간단하고, 견고하고, 빠릅니다. 이 코드는 HTML 태그 및 인코딩과 관련하여 다음과 같이 작동합니다.

$html = '<p>äöü</p><p>ß</p>';

누구든지 오류를 발견하면 알려주십시오. 직접 사용하겠습니다.

편집 , 오류없이 작동하는 기타 유효한 옵션 (이미 제공된 것과 매우 유사 함) :

@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$saved_dom = trim($dom->saveHTML());
$start_dom = stripos($saved_dom,'<body>')+6;
$html = substr($saved_dom,$start_dom,strripos($saved_dom,'</body>') - $start_dom );

Furure에 이상한 일이 생기지 않도록 몸을 직접 추가 할 수 있습니다.

Thirt 옵션 :

 $mock = new DOMDocument;
 $body = $dom->getElementsByTagName('body')->item(0);
  foreach ($body->childNodes as $child){
     $mock->appendChild($mock->importNode($child, true));
  }
$html = trim($mock->saveHTML());