Stefan: Highlight Text

Hallo zusammen,

ich habe ein Problem,
ich will per PHP den gesuchten Text in einem text "highlighten".

das ist so weit kein Problem.

Nun habe ich aber einen Suchbegriff mit zwei "Worten": z.B:"vari li"
Der Text sieht beispielhalber so aus: "Variable light"

Den Suchstring will ich per "<span class="highlight">SUCHE</span> hervorheben.

Dies wollte ich so bewerkstelligen:

  
$userquery = "vari li";  
$text = "variable lighte";  
  
function highlight($userquery,$text){  
    foreach (explode(" ", utf8_decode($userquery)) as $query) {  
	if (preg_match("/" . $query . "/i", $text, $match)) {  
	    $text = str_replace($match[0], "<span class='highlight'>" . $match[0] . "</span>", $text);  
	}  
    }  
}  
  
echo highlight($userquery,$text);  

nun bekomme ich als Antwort das hier:

  
<span class='high<span class='highlight'>li</span>ght'>vari</span>able <span class='highlight'>li</span>ghte  

Und wie man sieht wurde da der HTML Code falsch ersetzt.
Wie kann man es verhindern, dass der "highlightecode" mit ersetzt wird?
Ich kann per Regex meinem Wissen nach nicht so ersetzen, dass nur das was nicht innerhalb eines Span ist ersetzt wird? Oder doch? Gibts ne alternative Lösung?

DANKE im Voraus!

  1. Tach!

    foreach (explode(" ", utf8_decode($userquery)) as $query) {

    Welche Bewandnis hat das utf8_decode() hier? Damit gehen dir potentiell Zeichen verloren. Wenn du UTF-8 haben willst, mit allem drum und dran, dann solltest du nicht in eine "geringere" Kodierung umkodieren.

    if (preg_match("/" . $query . "/i", $text, $match)) {

    stripos() würde das Ergebnis kostengünsiger liefern.

    $text = str_replace($match[0], "<span class='highlight'>" . $match[0] . "</span>", $text);

    Aber beide Zeilen brauchst du nicht. Zudem fehlt die Beachtung des Kontextwechsels beim Einsetzen der Benutzereingabe in den HTML-Code.

    nun bekomme ich als Antwort das hier:
    <span class='high<span class='highlight'>li</span>ght'>vari</span>able <span class='highlight'>li</span>ghte
    Und wie man sieht wurde da der HTML Code falsch ersetzt.

    Ja, so arbeitet str_replace(), sowohl wenn man es wiederholt auf den bereits ersetzten Text anwendet, also auch wenn man die Array-Variante nimmt. Die Funktion strtr() in ihrer zweiten Variante kann das besser.

    Du brauchst zunächst ein Array, bestehend aus den Suchtextteilen als Keys und den Ersatztexten als Werte. Das kannst du in der foreach-Schleife erzeugen (htmlspecialchars() nicht vergessen und das utf8_decode() weglassen). Anschließend ein strtr()-Aufruf und alles wird gut.

    Dummerweise arbeitet strtr() nicht case-insensitive. Es gibt aber in den Userkommentaren eine solche Version. Probier aber mal, ob sie ebenfalls wie strtr() das bereits Ersetzte in Ruhe lässt.

    dedlfix.

  2. Hallo Stefan!

    Nachdem mich dieses Problem interessiert hat, habe ich mich jetzt hingesetzt und dank dedlfix' Hinweis auf strtr(); habe ich Deine 'Aufgabe' nun lösen können:

      
        // Festlegung von Text und Suchstring:  
      
        $text = 'variable lighte';  
      
        $suchmuster_input = 'vari li';  
      
        $suchmuster = htmlspecialchars($suchmuster_input);  
      
        // Der Suchstring wird in die einzelnen Teile zerlegt:  
      
        $suchen = explode(' ',$suchmuster);  
      
        // Ein Array mit den zu ersetzenden Stringteilen als Key  
        // und den Ersetz-Pattern als Werte wird gebildet:  
      
        $ersetzungen = array();  
      
        $arrayindex = 0;  
      
        foreach ( $suchen AS $suche )  
      
            {  
      
                $ersetzungen[$suche] = '<span class="highlight">'.$suche.'</span>';  
      
            }  
      
        // Nun kann der Text umgewandelt werden  
        // und das Highlighting stattfinden:  
      
        $text_neu = strtr($text, $ersetzungen);  
      
    
    

    Ich hoffe, ich konnte helfen! Danke dedlfix für den Denkanstoß!

    Mit freundlichen Grüßen

    Hugo Egon Balder

    1. Jetzt hab ich Idiot vergessen, den letzten Teil meiner gescheiterten Lösungsversuche aus dem Code zu löschen.

      Das  $arrayindex = 0; hat keine Funktion und kann weg!

      Das sind immer die Momente, wo ich es so hasse, dass dieses Forum keine Editiermöglichkeit hat. Zumindest ein paar Minuten nach einem Posting. =/

    2. Tach!

      // Festlegung von Text und Suchstring:
          $text = 'variable lighte';
          $suchmuster_input = 'vari li';
          $suchmuster = htmlspecialchars($suchmuster_input);

      Das ist die falsche Stelle für das htmlspecialchars(). Und selbst wenn der $text an der Stelle bereits HTML-gerecht aufgearbeitet sein sollte, ist das für ihn zu früh geschehen. Dann gibts dasselbe Problem, wie es der OP bereits hat. An dieser Stelle findet Verarbeitung statt und noch keine Ausgabe, also müssen/sollten Rohdaten vorliegen.

      foreach ( $suchen AS $suche )
                  $ersetzungen[$suche] = '<span class="highlight">'.$suche.'</span>';

      Erst jetzt wird HTML produziert, also darf (und muss) auch erst jetzt das htmlspecialchars() zum Einsatz kommen.

      Und wer jetzt aufgepasst hat, hat einen möglichen Denkfehler nicht übersehen. Es ist nicht bekannt, wo der $text herkommt. Ist es Rohtext aus einer sicheren Quelle oder eine Nutzereingabe? Selbst wenn es "sicherer" Text wäre, muss man die HTML-eigenen Zeichen berücksichtigen. Wenn man das nach der Ersetzung vornimmt, sind die Tag- und Attribut-Begrenzer des Markups der ersetzten Textstellen ebenfalls betroffen - was jedoch nicht sein soll. Hinterher den Text zu parsen, um festzustellen, was gut und was böse ist, ist mindestens mal aufwendig, wenn nicht gar unmöglich, weil nicht herauszufinden ist, was als Zeichen und was als HTML-Code beabsichtigt war.

      Unter der Voraussetzung, dass der $text kein Markup enthält (sonst ist sowieso alles anders, weil das OP-Ersetzungsproblem bestehen bleibt), wäre dann doch die htmlspecialchars()-Idee eine mögliche Lösung, dann muss das aber vor dem Beginn der Ersetzungsprozedur mit $text _und_ $suchmuster_input (und nur da, nicht mehr in der foreach-Schleife) passieren. Jetzt sind beide Texte gleichmäßig "verunstaltet" und Minus mal Minus ergibt Plus, sozusagen - wenn ich nicht noch etwas übersehen habe.

      dedlfix.

      1. Hi dedlfix!

        Ich habe das unter der Voraussetzung gemacht, dass $text schon fixer Bestandteil der Ressource ist und $suchmuster_input von aussen kommt, also einem $_POST[...] oder $_GET[...]gleich kommt.

        Mit freundlichen Grüßen

        Hugo Egon Balder

  3. Ich halte es genrell für gut, die Perl kompatiblen Such-/Ersetzefunktionen zu nutzen (im PHP Handbuch unter der überschrift "PCRE — Reguläre Ausdrücke (Perl-kompatibel)" zu finden. Die sind Leistungsfähiger. Allerdings ist die Syntax etwas komplizierter. Wenn man es einmal lernt und regelmäßig einsetzt ist das aber kein Problem.

    Gruß, Gerald