Как сбалансировать теги с помощью PHP - PullRequest
2 голосов
/ 12 ноября 2009

В приведенной ниже строке я хочу заменить <!--more--> некоторым текстом, FOOBAR, а затем обрезать строку.

<p>The quick <a href="/">brown</a> fox jumps <!--more-->
over the <a href="/">lazy</a> dog.</p>

Я дошел до этого пункта:

<p>The quick <a href="/">brown</a> fox jumps FOOBAR

... но, как видите, тег <p> не закрыт. Любые идеи о том, как я мог бы последовательно сбалансировать теги? Я довольно новичок в PHP.

Массив, с которым я работаю, выглядит следующим образом:

array(2) {
  [0]=>
  string(50) "<p>The quick <a href="/">brown</a> fox jumps "
  [1]=>
  string(45) " over the <a href="/">lazy</a> dog.</p>"
}

Ответы [ 5 ]

4 голосов
/ 01 октября 2011

Вы можете использовать функцию wordpress force_balance_tags. Реализация живет здесь: -

http://core.trac.wordpress.org/browser/trunk/wp-includes/formatting.php

Это отдельная функция, которую вы можете просто скопировать + вставить в свой код.

function force_balance_tags( $text ) {

Простое использование

$bad_text = "<div> <p> some text </p> " ;

echo force_balance_tags ($ bad_text);

, поскольку это часть WordPress, он опробован и протестирован и лучше, чем adHoc regex macthing solutions.

2 голосов
/ 12 ноября 2009

Если возможно, я бы предложил разбирать HTML в DOM и работать с ним таким образом, проходя через текстовые узлы до тех пор, пока вы не найдете эту строку, затем обрезая текстовый узел и удаляя любые дальнейшие дочерние узлы после тот (оставляя родителя нетронутым). Затем повторно сериализовать DOM в HTML.

1 голос
/ 13 ноября 2009

Я еще не полностью проверил это, но это работает, по крайней мере, для вашего примера. Принимает правильно сформированный XML.

<?php
$reader = new XMLReader;
$writer = new XMLWriter;

// load the XML string into the XMLReader
$reader->xml('<p>The quick <a href="/">brown</a> fox jumps <!--more--> over the <a href="/">lazy</a> dog.</p>');
// write the new XML to memory
$writer->openMemory();
$done = false;

// XMLReader::read() moves the current read location to the next node
while ( !$done && $reader->read()) {
    // choose action based on the node type
    switch ($reader->nodeType) {
        case XMLReader::ELEMENT:
            // read an element, so write it back to the output
            $writer->startElement($reader->name);
            if ($reader->hasAttributes) {
                // loop through all attributes and write them
                while($reader->moveToNextAttribute()) {
                    $writer->writeAttribute($reader->name, $reader->value);
                }
                // move back to the beginning of the element
                $reader->moveToElement();
            }
            // if the tag is empty, close it now
            if ($reader->isEmptyElement) {
                $writer->endElement();
            }
            break;
        case XMLReader::END_ELEMENT:
            $writer->endElement();
            break;
        case XMLReader::TEXT:
            $writer->text($reader->value);
            break;
        case XMLReader::COMMENT:
            // you  can change this to be more flexible if you need
            // e.g. preg_match, trim, etc.
            if (trim($reader->value) == 'more') {

                // write whatever you want in here. If you have xml text
                // you want to write verbatim, use writeRaw() instead of text()
                $writer->text('FOOBAR');

                // this is where the magic happens -- endDocument closes
                // any remaining open tags
                $writer->endDocument();
                // stop the loop (could use "break 2", but that gets confusing
                $done = true;
            }
            break;
    }
}
echo $writer->outputMemory();
0 голосов
/ 12 ноября 2009

Вы должны найти все теги открытыми, но не закрытыми, перед текстом заполнителя. Вставьте новый текст, как вы делаете сейчас, а затем закройте теги.

Вот небрежный пример. Я думаю, что этот код будет работать со всем допустимым HTML, но я не уверен. И это, безусловно, примет недопустимую разметку. но все равно:

$h = '<p>The quick <a href="/">brown</a> fox jumps <!--more-->
over the <a href="/">lazy</a> dog.</p>';

$parts = explode("<!--more-->", $h, 2);
$front = $parts[0];

/* Find all opened tags in the front string */
$tags = array();
preg_match_all("|<([a-z][\w]*)(?: +\w*=\"[\\w/%&=]+\")*>|i", $front, $tags, PREG_OFFSET_CAPTURE);
array_shift($tags); /* get rid of the complete match from preg_match_all */

/* Check if the opened arrays have been closed in the front string */
$unclosed = array();
foreach($tags as $t) {
    list($tag, $pos) = $t[0];
    if(strpos($front, "</".$tag, $pos) == false) {
        $unclosed[] = $tag;
    }
}    

/* Print the start, the replacement, and then close any open tags. */
echo $front;
echo "FOOBAR";
foreach($unclosed as $tag) {
    echo "</".$tag.">";
}

Выходы

<p>The quick <a href="/">brown</a> fox jumps FOOBAR</p>
0 голосов
/ 12 ноября 2009

Когда вы сформулируете проблему, это так просто:

str_replace('<!--more-->', 'FOOBAR', $original_text);

Возможно, если вы обновите свой вопрос, объяснив, какое отношение массив имеет ко всей проблеме, поможет интерпретировать правильный вопрос - (должна ли строка <!--more--> находиться в массиве?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...