Ein Samstag, an dem ich mich mal wieder zu Lernzwecken auf Stack Overflow umsah, um eigentlich etwas total anderes zu machen. Wie das aber manchmal so ist, blieb ich bei einer Fragestellung hängen, die mein Wissen über HTML und XML im Zusammenhang mit PHP ein wenig auf die Probe stellte.
replace symbols inside only one tag in php
https://stackoverflow.com/questions/77506617/replace-symbols-inside-only-one-tag-in-php
Wie auf Stack Overflow üblich, wurde direkt wieder mit regulären Ausdrücken als die ultimative Lösung um sich geworfen. Ja, das war vor vielen Jahren mal der bevorzugte und performantere Lösungsansatz. Seit der Version 5.6 verfügt PHP aber über seine eigenen DOM Klassen, um mit HTML und XML umgehen zu können. Diese Klassen sind erstmal performanter und sehr viel einfacher zu lesen, als ein aufwendiger regulärer Ausdruck, der Dir das Vorkommen eines Zeichens innerhalb eines bestimmten HTML Tags sucht.
Die Aufgabenstellung
Eigentlich ist die Aufgabe recht schnell formuliert: Finde alle Vorkommen eines bestimmten HTML Elements mit einem bestimmten Zeichen in dem dazugehörigen Text Knoten. Das ist aus meiner Sicht eine sehr einfache und verständliche Aufgabenstellung. Die Dudes auf Stack Overflow haben die Frage nach nur wenigen Minuten direkt geschlossen, da sie ihnen nicht detailliert genug war. Also genau der kleingeistige Bullshit, den man als Fragesteller auf Stack Overflow mittlerweile zu erwarten hat.
Beschäftigen wir uns aber mal mit dem Lösungsansatz.
Der Lösungsansatz
Die auf Stack Overflow verfügbare Antwort geht schon in die richtige Richtung. Zumindest erledigt sie den Job. Es geht aber auch sehr viel einfacher.
<?php
declare(strict_types=1);
namespace Marcel;
use DOMDocument;
use DOMXPath;
$text = "<strong>text</strong>more text<em>more text</em>additional text<span>text</span>";
$doc = new DOMDocument();
$doc->loadHTML($text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($doc);
$elements = $xpath->query('//em[contains(text(), "x")]');
if ($elements !== false) {
foreach ($elements as $element) {
$element->textContent = str_replace('x', '?', $element->textContent);
}
}
$text = $doc->saveHTML();
Der zu parsende Text stammt aus der Stack Overflow Frage. Was ich hier mache ist recht einfach: Wir laden den Text mittels der PHP Klasse DOMDocument. Gleichtzeitig sagen wir der Klasse, dass wir von HTML und DTD und dergleichen einfach nichts wissen wollen, indem wir die entsprechenden LIBXML Konstanten beim Laden des Textes mitgeben. Dies zwingt die DOMDocument Klasse dazu beim speichern des Textes die <html> und <body> Tags weg zu lassen, weil wir davon ja auch nichts wissen wollen.
Danach wird die PHP Klasse DOMXPath herangezogen, um mittels eines einfachen Query nach allen <em> Elementen zu suchen, die ein „x“ beinhalten. Würden wir an dieser Stelle in einem Text suchen, der kein <em> Element mit einem „x“ im Text Knoten vorhanden wäre, würde das Ergebnis schlichtweg false sein. Da wir aber ein solches Element im Text haben, finden wir es auch.
Entgegen der angebotenen Lösung auf Stack Overflow finden wir direkt alle <em> Elemente, die im Text Knoten ein „x“ besitzen. Alle <em> Element, die kein „x“ in ihrem Text Knoten besitzen, sind nicht betroffen und interessieren uns auch nicht. Daraus resultiert dann auch nur eine Iteration, die die gefundenen Elemente manipuliert.
Am Ende speichern wir den Text. Das war ’s. Nur eine Iteration. Performant AF!
Noch etwas zu Stack Overflow
Wie schnell möchte man bitte einen Anfänger demotivieren? Stack Overflow nur so: JA! Bin ich eigentlich der einzige, der Stack Overflow mehr und mehr meidet, weil sich dort irgendwie so eine gewisse toxische Stimmung breit gemacht hat, weil man als erste Reaktion auf eine recht einfache Frage erstmal erwarten muss, dass man als dumm hingestellt wird? Mittlerweile stelle ich meine Fragen eher wieder in Foren oder auf GitHub, als auf Stack Overflow.
Wie steht ihr mittlerweile zu Stack Overflow?