PHP hat so einige Funktion zum suchen und ersetzen von Textstrings mit und ohne die Hilfe von regulärer Ausdrücken. Hat man die nötige Geduld und das Wissen, kann man damit fast alles “klären und säubern”.
In Blogartikeln, die HTML wie Links und Bilder enthalten, wollte ich zusätzliche Links einparsen die mit bestimmten Keywords verknüpft sind.
Das Problem, was ich mit einem regulären Ausdruck nicht lösen konnte ist, dass auch Keywords innerhalb von Attributen wie title=”Keyword”, <img src=”…/keyword/…” /> oder <a href=”">Keyword</a> etc. ersetzte wurden.
Meine Lösung arbeitet mit zwei regulären Ausdrücken. Erst wird der Text an den HTML-Tags in Tokens zerlegt. Diese Tokens werden durchlaufen. Befindet sich der Algorithmus innerhalb eines HTML-Tags, so werden gefundene Keywords ignoriert. Ansonsten ersetzt.
Da der Text zwingend xml-konform sein muss, werden zuerst HTML-Short-Tags wie <img> und <br> in valides XML umgeformt. Dies wird ganz am Ende des Parsers wieder rückgängig gemacht.
Bei sehr schlechtem HTML lohnt sich eventuell der Einsatz von tidy_clean_repair.
/** * Suchen und ersetzen von Strings wobei HTML-Tags beachtet werden. * @param string $search zu suchender string oder callback function * @param string $replace zu ersetzender string * @param string $buffer zu durchsuchende Zeichenkette * @param boolean $ic ignoriere Schreibweise, default true * @return string */ function replace( $search, $replace, $buffer, $ic = true ) { /** * wandle short-tags um, erleichtert das Parsen * <img /> => <img />, <br /> => <br /> */ $buffer = preg_replace( '/<img ([^/>]+)(.*)( ?\/?)>/iU', '<img \\1\\2/>', $buffer ); $buffer = preg_replace( '/<br ( ?)(.?)/>/iU', '<br />', $buffer ); /** * matche alle html-tags */ preg_match_all( '/(< [^>]+>|[^< ]+)/i', $buffer, $tokens ); /** * durchlaufe gefundene blöcke */ if( count( $tokens[ 0 ] ) > 0 ) { $result = ''; $depth = 0; foreach( $tokens[ 0 ] as $token ) { /** * überprüfe ob wir innerhalb eines <img /> oder <a> tags sind */ if( $token[ 0 ] == '< ' || $depth != 0 ) { $data .= $token; /** * tag wird geöffnet */ if( substr( $token, 0, 5 ) == '<img ' || substr( $token, 0, 3 ) == '<a ' ) { $depth ++; } /** * tag wird geschlossen */ elseif( $token == '</img>' || $token == '</a>' ) { $depth --; } } else { /** * eigentlichen string suchen ... */ preg_match_all( '/\b' . $search . '\b/' . ( $ic ? 'i' : '' ) , $token, $matches ); if( count( $matches[ 0 ] ) > 0 ) { foreach( $matches[ 0 ] as $match ) { /** * ... und ersetzen, callback möglich */ $token = preg_replace( "/$match/", is_callable( $replace ) ? $replace( $match ) : $replace, $token ); } } $data .= $token; } } $buffer = $data; unset( $data ); } /** * wandle anfangs geänderte html tags zurück */ $buffer = str_replace( array( '>', '<br />' ), array( ' />', '<br />' ), $buffer ); return( $buffer ); } /** * test */ $buffer = '<p><img src="/teststring" title="Titel: Ein TestString" alt="Bild: TestString" /> dieser TestString wird ersetzte.<br /><strong>dieser TestString auch</strong> <a href="" title="Ziel TestString">hier geht es zu TestString</a>.</p>'; /** * replace */ $buffer = replace( 'teststring', '<a href="">Gefunden</a>', $buffer ); /** * replace mit callback */ $buffer = replace( 'teststring', create_function( '$s', 'return( strtoupper( $s ) );' ), $buffer ); print( $buffer ); |
Verbesserungsvorschläge sind willkommen!
Vielen Dank für diese wunderbare Funktion! Genau was ich gesucht habe – funktioniert einwandfrei :-)
Nachtrag: ich würde vielleicht auch noch die Shorttags ‘input’ und ‘hr’ umwandeln…
Macht Sinn. Ich werde das die Tage etwas anpassen.
Vielen Dank für die Funktion. Danach habe ich sehr lange gesucht.
@cusaro deine Anfänglichen Probleme hängen mit dem hie rim BLog verwendeten Syntax-Highlighter zusammen. Das ist leider noch immer ein komplexes Thema.