PHP remove_empty_tags

Passt ja ungefähr, und das hier scheint ja erledigt zu sein (dem Snippets Thread nach):

Wie wäre es noch mit einer Funktion, um nicht geschlossene Tags auch zu entfernen? Damit könnte man das hier verhindern
 
Passt ja ungefähr, und das hier scheint ja erledigt zu sein (dem Snippets Thread nach):
Jap, ist dochn schönes Teil geworden.
Hat laut Google auch schon Freunde gefunden. oO

Wie wäre es noch mit einer Funktion, um nicht geschlossene Tags auch zu entfernen?
Ihh wasn das?
Hm das wird aber nicht ganz einfach, da Du da rekursiv auf valide Verchachtelungen parsen musst. Höchstens so ganz "dumm" if(irgendwas_offenes_am_ende_nicht_zu) wegmit(). Würde zumindest für meine News-Lieferanten ausreichen. Meine "fix"-Scripte pro Lieferant sind meist größer als der ganze restliche Code der News-Importe. :-?

edit: OHA
unsere function hat einen bug und ist schuld an der misere!
PHP:
$str="<p><b>Sofort gelöscht<br>";
$tags = Array('b','p');
echo remove_empty_tags ($str,$tags);
// ausgabe: <p><b>Sofort gelöscht
:arrow: Der entfernt <br> weil er es als <b> ekennt

edit2: BUG2
PHP:
<p><b>Sofort gelöscht<br >
</b></p>
wird zu
PHP:
<p><b>Sofort gelöscht</p>
Kann das einer erklären/fixen? oO

edit3: *raff*
Erklärung hängt mit edit1 zusammen ... er erkennt <br> als <b> und entfernt dann <br></b> (weil er das als <b></b> sieht). :ugly:
 
:arrow: Der entfernt <br /> weil er es als <b /> ekennt
Irgendwie habe ich bloss verbummelt, warum ich da ein Blank haben wollte, da hätte ich besser argumentieren müssen. Fehlt einfach nur das \s von meiner früheren Version. Habe ich gleich überall hineineditiert. <-- Erledigt. (Auch im Snippets-Thread :sing:)
 
Zuletzt bearbeitet:
Wie wäre es noch mit einer Funktion, um nicht geschlossene Tags auch zu entfernen?
Oder auch automatisch zu schließen, was ja auch nicht schlecht wäre. Das dürfte eventuell eine Lösung dazu sein, wobei per Parameter gesteuert wird, ob entfernt oder geschlossen wird.
PHP:
// Nicht geschlossene Tags entfernen oder schließen
function handle_unclosed_tags ($string, $close=false) {
    $p_tag = '<[^>]*[^\/]>';
    $split =
        preg_split('/('.$p_tag.')/', $string, -1,
            PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    $p_id = '[A-Za-z_\-][A-Za-z0-9_\-]*';
    $p_o_long_tag = '<('.$p_id.')(?:\s[^>]*[^\/])?>';
    $p_cl_long_tag = '<\/'.$p_id.'>';
    $openTags = array();
    $stringArray = array();
    foreach ($split as $i => $elem) {
        $stringArray[$i] = array($elem, true);
        $match = array();
        if (preg_match('/'.$p_o_long_tag.'/AS', $elem, $match)) {
            $stringArray[$i] = array($elem, false);
            array_unshift($openTags, array('</'.$match[1].'>', $i));
        } elseif (preg_match('/'.$p_cl_long_tag.'/AS', $elem)) {
            $stringArray[$i] = array($elem, false);
            $closeTags = "";
            for (
                $openTag = array_shift($openTags);
                isset($openTag[0]) && $elem != $openTag[0];
                $openTag = array_shift($openTags)
            ) {
                if ($close) {
                    $closeTags .= $openTag[0];
                    $stringArray[$openTag[1]][1] = true;
                }
            }
            if (isset($openTag[0]) && $elem == $openTag[0]) {
                $stringArray[$openTag[1]][1] = true;
                $stringArray[$i] = array(
                    $closeTags . $elem,
                    true
                );
            }
        }
    }
    $result = "";
    foreach ($stringArray as $stringElem) {
        if ($stringElem[1]) {
            $result .= $stringElem[0];
        }
    }
    return $result;
}
PHP:
// Aufruf
$string = "<p><b>Sofort gelöscht</p>";
echo "Bsp.: ".htmlspecialchars($string)."\n";
// Entfernen nicht-geschlossener Tags
echo handle_unclosed_tags($string)."\n";
// Automatisches Schließen nicht-geschlossener Tags
echo handle_unclosed_tags($string, true)."\n";
 
Wie es sich herausgestellt hat, ist preg_replace nicht geeignet, um in reflexiven Zuweisungen eingesetzt zu werden.

Entweder sind in einem nicht-matchenden Pattern nicht-matchende capturing groups eingebaut oder immer noch bei nicht-matchendem Pattern ist der String einfach zu lang, dann liefert preg_replace NULL zurück anstelle des unveränderten Strings.

Es heißt zwar
Falls Übereinstimmungen gefunden wurden, wird die neue subject zurückgegeben, andernfalls wird subject unverändert zurückgegeben
aber eben auch :ugly:
oder NULL, falls ein Fehler auftrat.

Deshalb ist bei der Verwendung von preg_replace der Fall des Rückgabewerts zusätzlich abzufragen. Im Snippet remove_empty_tags ist der while-Schleifenbedingung deshalb noch eine zusätzliche Abfrage mitzugeben:
PHP:
// Leere Tags entfernen
function remove_empty_tags ($string, $tags) {
    $p_o_tag = '<('.implode('|', $tags).')(?:\s[^>]*[^\/])?';
    $p_o_tag_short_tag = '\/>';
    $p_o_tag_long_tag = '>';
    $p_empty = '(?: |\x00|\xa0|\s)*';
    $p_cl_tag = '<\/\\1>';
    $pattern =
        $p_o_tag
        .'(?:'.$p_o_tag_short_tag
        .'|'.$p_o_tag_long_tag.$p_empty.$p_cl_tag.')';
    while (
        $string !=
            ($val = preg_replace('/'.$pattern.'/iS', '', $string))
        && is_string($val)
    ) {
        $string = $val;
    }
    return $string;
}

// Aufruf
$string = "<p><p><b><strong>    <p></p><p />  </strong></b></p></p>";
$string = "<div>"{$string}"</div>";
$tags = array('p', 'strong', 'b');
echo remove_empty_tags($string, $tags)."\n";
 
Ist zwar ein alter thread, aber ich habe mich jetzt sogar extra registriert, nur um einmal kräftig:
DANKE
zu sagen. Nachdem ich stundenlang nach genau einer solchen feinen Funktion gesucht hatte, habe ich sie hier bei euch gefunden. Super!

Hätte es ja selber probiert, hab aber bisher zuwenig Erfahrung mit Regex. ;)