2010-02-10 20 views
8

PHP'yi Kullanma Bir WYSIWYG düzenleyicisinden geçirilen bir HTML dizesini almaya ve önceden yüklenmiş bir HTML belgesinin içindeki öğelerin alt öğelerini yeni HTML ile değiştirmeye çalışıyorum.PHP DOMDocument HTML dizgisi olan DOMElement çocuğunun yerine koyuluyor

Şimdiye kadar, kimliğiyle değiştirmek istediğim öğeyi tanımlayan belgeyi yüklüyorum, ancak bir HTML'yi bir DOMElement içine yerleştirilebilecek bir şeye dönüştürme işlemi beni reddediyor.

libxml_use_internal_errors(true); 

$doc = new DOMDocument(); 
$doc->loadHTML($html); 

$element = $doc->getElementById($item_id); 
if(isset($element)){ 
    //Remove the old children from the element 
    while($element->childNodes->length){ 
     $element->removeChild($element->firstChild); 
    } 

    //Need to build the new children from $html_string and append to $element 
} 

cevap

13

, (tüm alt düğümler eleman temizledikten sonra) bunu yapabilirsiniz:

$ html_string XML olarak çözümlenen edilemiyorsa
$fragment = $doc->createDocumentFragment(); 
$fragment->appendXML($html_string); 
$element->appendChild($fragment); 

, başarısız olur. Bunu yaparsa, daha az katı olan loadHTML() öğesini kullanmanız gerekir. Ancak, şeritlemeniz gereken parçanın etrafına öğeler ekler.

PHP'nin aksine, Javascript bunu kolayca yapmanızı sağlayan innerHTML özelliğine sahiptir. Bir proje için buna benzer bir şeye ihtiyacım vardı, bu yüzden PHP’nin DOMElementini Javascript benzeri içHTML erişimini içerecek şekilde genişlettim. Bununla beraber

Eğer innerHTML özelliğini erişebilir ve JavaScript gibi yalnızca değiştirebilirsiniz:

echo $element->innerHTML; 
$elem->innerHTML = '<a href="http://example.org">example</a>'; 

Kaynak: Bu eski bir iplik olduğunu biliyorum (ama üzerinde cevap http://www.keyvan.net/2012/11/php-domdocument-replace-domelement-child-with-html-string/

+0

@Greg, katkılarımın nereye gittiğine karar vermemeli miyim? Ve ne zamandan beri dünya için konuştunuz? Katkılarımın bir kısmı StackOverflow'tan silindikten ve gizlendikten sonra katkılarımı kendi blog'uma taşımaya karar verdim. Bu şekilde tutmak istiyorum, lütfen değişikliği geri alın. – Keyvan

+0

Potansiyel bir çözümün bağlantısı her zaman bekler, ancak lütfen bağlantı etrafında bir bağlam ekleyin, böylece diğer kullanıcılarınızın ne olduğu ve neden orada olduğu hakkında bir fikirleri olur. Hedef siteye ulaşılamaması veya sürekli olarak çevrimdışı olması durumunda, her zaman önemli bir bağlantının en alakalı bölümünü belirtin. Kaynak: [* Nasıl cevap verilir *] (http://stackoverflow.com/questions/how-to-answer) – Greg

+0

@Greg, Kılavuzların farkındayım. Buradaki cevabı orijinal olarak yayınladım ve diğer sitemlerin bu sitede nasıl ele alındığına bağlı olarak siteme taşıdım - yukarıda belirttiğim gibi silinmiş ve benden gizlenmişler. Neden bir bağlantıya bu kadar çok itiraz ediyorsun? Bu sitenin içerik oluşturucularından birinden düşünce için bazı yiyecekler http://www.codinghorror.com/blog/2009/08/are-you-a-digital-sharecropper.html "Katkılarınız iptal edilebilir, silinebilir veya kalıcı olarak kullanılabilir mi?" rızanız olmadan çevrimdışı aldınız mı? " Stackoverflow'ta: evet. Kendi sitemde: hayır. – Keyvan

1

Sen kod parçasının üzerinde loadHTML() kullanabilir ve daha sonra orijinal DOM ağacına çıkan oluşturulan düğümleri ekleyin. HTML dize XML olarak çözümlenebilir Eğer

+0

Yeni HTML belgesini kullanarak yeni bir DOMDocument oluşturmayı ve sonra yeni Belge'nin beden etiketini almayı ve bunları orjinal DOM'a eklemeyi önerir misiniz? Yoksa eksik olduğum başka bir loadHTML() işlevi var. – AWinter

+0

SaveHTML() veya loadHTML() gibi şeyler yaparken, html ve body etiketlerinin otomatik olarak nasıl ekleneceğinden gerçekten nefret ediyorum. Onları kesip çıkaracak bir sarıcı yazmaktan başka kolay bir çözüm var mı? –

0

Bu da bunun için bir çözüm arıyor çünkü). Kullanırken içeriği tek bir satır ile değiştirmek için kolay bir yöntem yaptım. Yöntemi daha iyi anlamak için, aynı zamanda adlandırılmış işlevler ekliyorum.

Bu, şimdi benim kütüphanemizin bir parçası, bu yüzden buradaki tüm işlev isimlerinin nedeni budur, tüm işlevler 'su' öneki ile başlar.

Kullanımı çok kolay ve çok güçlü (ve oldukça az kod).

function suSetHtmlElementById(&$oDoc, &$s, $sId, $sHtml, $bAppend = false, $bInsert = false, $bAddToOuter = false) 
{ 
    if(suIsValidString($s) && suIsValidString($sId)) 
    { 
    $bCreate = true; 
    if(is_object($oDoc)) 
    { 
     if(!($oDoc instanceof DOMDocument)) 
     { return false; } 
     $bCreate = false; 
    } 

    if($bCreate) 
     { $oDoc = new DOMDocument(); } 

    libxml_use_internal_errors(true); 
    $oDoc->loadHTML($s); 
    libxml_use_internal_errors(false); 
    $oNode = $oDoc->getElementById($sId); 

    if(is_object($oNode)) 
    { 
     $bReplaceOuter = (!$bAppend && !$bInsert); 

     $sId = uniqid('SHEBI-'); 
     $aId = array("<!-- $sId -->", "<!--$sId-->"); 

     if($bReplaceOuter) 
     { 
     if(suIsValidString($sHtml)) 
     { 
      $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); 
      $s = $oDoc->saveHtml(); 
      $s = str_replace($aId, $sHtml, $oDoc->saveHtml()); 
     } 
     else { $oNode->parentNode->removeChild($oNode); 
       $s = $oDoc->saveHtml(); 
       } 
     return true; 
     } 

     $bReplaceInner = ($bAppend && $bInsert); 
     $sThis = null; 

     if(!$bReplaceInner) 
     { 
     $sThis = $oDoc->saveHTML($oNode); 
     $sThis = ($bInsert?$sHtml:'').($bAddToOuter?$sThis:(substr($sThis,strpos($sThis,'>')+1,-(strlen($oNode->nodeName)+3)))).($bAppend?$sHtml:''); 
     } 

     if(!$bReplaceInner && $bAddToOuter) 
     { 
      $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); 
      $sId = &$aId; 
     } 
     else { $oNode->nodeValue = $sId; } 

     $s = str_replace($sId, $bReplaceInner?$sHtml:$sThis, $oDoc->saveHtml()); 
     return true; 
    } 
    } 
    return false; 
} 

// A function of my library used in the function above: 
function suIsValidString(&$s, &$iLen = null, $minLen = null, $maxLen = null) 
{ 
    if(!is_string($s) || !isset($s{0})) 
    { return false; } 

    if($iLen !== null) 
    { $iLen = strlen($s); } 

    return (($minLen===null?true:($minLen > 0 && isset($s{$minLen-1}))) && 
      $maxLen===null?true:($maxLen >= $minLen && !isset($s{$maxLen}))); 
} 

Bazı bağlam fonksiyonları:

function suAppendHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false); } 

function suInsertHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true); } 

function suAddHtmlBeforeById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true, true); } 

function suAddHtmlAfterById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false, true); } 

function suSetHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, true); } 

function suReplaceHtmlElementById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, false); } 

function suRemoveHtmlElementById(&$s, $sId, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, null, false, false); } 

Nasıl kullanılır: Aşağıdaki örneklerde

, ben varsayalım İşte

kodudur zaten bir variab içine yüklenen içerik var le, $sMyHtml'u aradı ve $sMyNewContent değişkeni bazı yeni html içeriyor. $sMyHtml değişkeni, 'example_id' kimliğine sahip olan/içeren bir öğe içerir.

// Example 1: Append new content to the innerHTML of an element (bottom of element): 
if(suAppendHtmlById($sMyHtml, 'example_id', $sMyNewContent)) 
{ echo $sMyHtml; } 
else { echo 'Element not found?'; } 

// Example 2: Insert new content to the innerHTML of an element (top of element): 
suInsertHtmlById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 3: Add new content ABOVE element: 
suAddHtmlBeforeById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 3: Add new content BELOW/NEXT TO element: 
suAddHtmlAfterById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 4: SET new innerHTML content of element: 
suSetHtmlById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 5: Replace entire element with new content: 
suReplaceHtmlElementById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 6: Remove entire element: 
suSetHtmlElementById($sMyHtml, 'example_id'); 
+0

@brasofilo, tüm gönderilerimi değiştirecek misin? Şapşal seni! – Codebeat

+0

Hayır ... Bunu yapacağınızı umuyordum **) http://meta.stackexchange.com/questions/28416/what-is-the-policy-on-signatures-and-links- in-answers – brasofilo

+0

@brasofilo, üzgünüm, üzgünüm, yapacak daha önemli işlerim var. – Codebeat

1

kabul edilen geçerli bir cevap appendXML() kullanılmasını önerir, ancak bu tür özgün soru belirtildiği gibi bir WYSISYG editörü döndü ne kadar karmaşık html işlemez kabul eder. Önerilen loadHTML() bunu ele alabilir. ama hiç kimse nasıl olduğunu göstermedi.

Kodlama sorunları, "Belge Parçası boş" uyarıları ve "Yanlış Belge Hatası" hatalarını ele alan orijinal sorunun en iyi/doğru cevabı olduğuna inanıyorum. Önceki yanıtlardaki ipuçlarını izledikten sonra onları buldum.

Bu, WordPress kenar çubuğu içeriğini bir gönderinin $ içeriğine ekleyen bir siteden gelen koddur. $ Doc'ın orijinal soruda $ doc'ın tanımlandığı şekle benzer geçerli bir DOMDocument olduğunu varsayar. Ayrıca, $ öğesinin, sonradan kenar çubuğunu (veya herhangi bir şekilde) eklemek istediğiniz etiket olduğunu varsayar.

// Now because we have moved the post content into a full document, we need to get rid of the 
    // extra elements that make it a document and not a fragment 
    $body = $doc->getElementsByTagName('body'); 
    $body = $body->item(0); 

    // If you need an element with a body tag, you can do this. 
    // return $doc->savehtml($body); 

    // Extract the html from the body tag piece by piece to ensure valid html syntax in destination document 
    $bodyContent = ''; 
    foreach($body->childNodes as $node) { 
      $bodyContent .= $body->ownerDocument->saveHTML($node); 
    } 
    // Now return the full content with the new content added. 
    return $bodyContent; 
: Eğer gövde etiketleri ile tam HTML dokümanının kaynağı ve, bununla daha yerel html üretebilir değil ne olmasını istemiyorsanız bu Sonuçta
  // NOTE: Cannot use a document fragment here as the AMP html is too complex for the appendXML function to accept. 
      // Instead create it as a document element and insert that way. 
      $node = new DOMDocument(); 
      // Note that we must encode it correctly or strange characters may appear. 
      $node->loadHTML(mb_convert_encoding($sidebarContent, 'HTML-ENTITIES', 'UTF-8')); 
      // Now we need to move this document element into the scope of the content document 
      // created above or the insert/append will be rejected. 
      $node = $doc->importNode($node->documentElement, true); 
      // If there is a next sibling, insert before it. 
      // If not, just add it at the end of the element we did find. 
      if ( $element->nextSibling) { 
       $element->parentNode->insertBefore($node, $element->nextSibling); 
      } else { 
       $element->parentNode->appendChild($node); 
      } 

, yapılır
+0

Bu çözümü paylaştığınız için teşekkür ederiz! ÇALIŞIYOR bir cazibe gibi! – Damneddani

+0

@Damneddani Savehtml'nin ($ body), HTML'yi bir body etiketi ile döndürdüğünü unutmayın. Html'yi başka bir sayfaya eklerseniz, bu geçersiz bir html üretecektir. Böyle bir şey yapmayı deneyin: $ rootContent = ''; foreach ($ rootNode-> çocuk $ düğümü olarak adlandırılır) { $ rootContent. = $ RootNode-> ownerDocument-> saveHTML ($ node); } // Hiçbir içerik, kenar çubuğu içeriği eklendiğinde geri dönülmez. dönüş $ rootContent; –

İlgili konular