PHP: finde in $content alle <p> und erweitere das 3, 6, 9 durch X
noob-2000 (Auf dem Schlauch stehen Profi)
- php
Hallo,
ich habe eine Frage an die Experten. Ich habe in PHP die Variable $content diese enthält den gesamten "Text" inhalt mit allen HTML Formatierungen. Ich möchte in jedem 1/3 des Textes (nach dem </p> oder vor dem <p>)ein Javascript snippet einfügen.
Mein bisheriger Ansatz war mit strpos mich durch den $content durch zu Tasten, eine Gesamtzahl an <p> zu haben. Die Zahl wird dann 1/3 um die JS ein zu fügen. Dann die erste Position mit substr_replace zu erweitern, und wieder mit strpos durch den Text (die Position hat sich jetzt ja wieder geändert). Bis alle positionen ersetzt sind.
Gefühlt ist das "von hinten durch die Brust ins Auge" habt Ihr da eine bessere Idee?
Hallo noob-2000,
ein JS Snippet nach 1/3 und 2/3 - das stinkt nach einem Werbebanner…
Ich nehme an, der Content wird auf Bausteinen zusammengesetzt und es ist erst im Nachhinein erkennbar, wo die "beste" Position für die Snippets ist. Der Ansatz, über die Position zu gehen, ist allerdings nicht unbedingt optimal, die Paragraphen können unterschiedlichste Höhe haben.
In PHP sitzt Du doch an der Quelle; kann man da nicht auf inhaltlicher Ebene bessere Möglichkeiten schaffen? Beispielsweise könnte man bei der Generierung von $content an den potenziellen Snippet-Einsetzpunkten einen speziellen Kommentar einsetzen, sowas wie <!-- INSERT YOUR SH1T HERE -->
.
Die Suche nach </p> ist noch machbar, bei <p> ist es problematischer, weil es ja auch sowas wie <p class="foo"> geben kann. D.h. du müsstest nach /<p[> ]/ suchen.
Nun ja, jedenfalls kannst Du darüber alle potenziellen Punkte finden. Deren Positionen steckst Du in ein aufsteigend sortiertes Array. Die Drittelung kannst Du nun auf Basis der Indexposition im Text machen (d.h. wenn l = strlen($content), dann berechne l/3 und 2l/3 und suche im Array die Einträge, die diesen Werten am nächsten kommen), oder auf Basis der Paragraphenanzahl. D.h. du könntest bei 7 Paragraphen dein Snippet hinter den zweiten (7/3=2,33 -> 2) und fünften (27/3=4,66 -> 5) setzen, unabhängig von der Paragraphengröße.
Wenn Du deine Snippets von hinten nach vorne einsetzt statt von vorne nach hinten, dann kann es Dir egal sein, dass sich durch das Einsetzen die Positionen in $content verschieben.
Rolf
Hallo Rolf,
danke für deine schnelle Antwort. Ich gehe direkt mal darauf ein.
ein JS Snippet nach 1/3 und 2/3 - das stinkt nach einem Werbebanner…
Da hast du ein gutes Näschen bewiesen, vollkommen korrekt.
kann man da nicht auf inhaltlicher Ebene bessere Möglichkeiten schaffen?
Wäre möglich, allerdings jetzt alle Artikel zu editieren und die "optimale" Position zu finden... Da lieber automatisch nach einem Absatz, da stören sie am wenigsten.
Die Suche nach </p> ist noch machbar, bei <p> ist es problematischer, weil es ja auch sowas wie <p class="foo"> geben kann. D.h. du müsstest nach /<p[> ]/ suchen.
Guter Hinweis, darüber habe ich garnicht nachgedacht.
Nun ja, jedenfalls kannst Du darüber alle potenziellen Punkte finden. Deren Positionen steckst Du in ein aufsteigend sortiertes Array. Die Drittelung kannst Du nun auf Basis der Indexposition im Text machen (d.h. wenn l = strlen($content), dann berechne l/3 und 2l/3 und suche im Array die Einträge, die diesen Werten am nächsten kommen), oder auf Basis der Paragraphenanzahl. D.h. du könntest bei 7 Paragraphen dein Snippet hinter den zweiten (7/3=2,33 -> 2) und fünften (27/3=4,66 -> 5) setzen, unabhängig von der Paragraphengröße.
Geniale Idee, so schön einfach, Danke
Hast du noch eine alternative für strpos um alle </p> zu finden?
Hallo,
Hast du noch eine alternative für strpos um alle </p> zu finden?
ja, Gunnars Vorschlag mit dem DOM-Parser. Wäre auch meine erste Idee gewesen.
Immer eine Handbreit Wasser unterm Kiel
Martin
Hallo noob-2000,
dazu solltest Du nicht strpos benutzen, sondern preg_match_all. Das bekommt eine Regex und liefert dann ein relativ kompliziertes Array mit allen Treffern.
preg_match_all("!<p[ >]|</p>!", $content, $matches, PREG_OFFSET_CAPTURE);
var_dump($matches[0]);
!<p[ >]|</p>!
ist die Regex (regular expression). Die ! vorn und hinten sind Teil der Regex-Syntax; eine Regex hat Begrenzerzeichen. Das ist der von PHP verwendeten Regex-Library geschuldet. Als Begrenzer nimmt man klassisch das / Zeichen, aber weil wir auch nach / suchen wollen (in </p>), verwende ich ein anderes. Man ist da relativ frei in der Auswahl.
Der senkrechte Strich | ist ein "Oder", suche nach <p[ >]
ODER </p>
. Bei </p>
ist es einfach, da sind keine Regex-Steuerzeichen drin, das ist einfach diese Zeichenfolge, nach der er sucht. In <p[ >]
werden die eckigen Klammern verwendet, das definiert eine Menge von Zeichen, von denen eins passen soll. Hier sind's nur 2, d.h. dieser Teil der Regex trifft auf <p
oder <p>
zu.
Man hätte genauso gut !<p |<p>|</p>!
schreiben können, das würde zum gleichen Ergebnis führen.
Weil die Regex keine Gruppen enthält (runde Klammern), gibt es pro Suchtreffer immer nur ein Ergebnis, deswegen enthält das $matches-Array nur einen Eintrag. Dieser eine Eintrag ist aber ein Array, mit einem Element pro gefundenem Treffer, und dieses Treffer-Element ist wieder ein Array mit 2 Einträgen: gefundener Text an Stelle 0, Position des Fundes an Stelle 1.
$matches[0][0][0] würde also den Text des ersten Treffers liefern, und $matches[0][0][1] dessen Anfangsposition im String. $matches[0][6][1] wäre die Position des siebten Treffers.
All das unter dem Vorbehalt, dass ein HTML Dokument etwas ist, was man mit einfachen Stringfunktionen besser nicht durchsucht. Ein Parser, der Dir ein DOM liefert, wäre eigentlich besser. Es ist in PHP nur nicht trivial, einen zu verwenden.
Der eingebaute DOM Parser: die DOMDocument-Klasse (Gunnar um 16:17 Uhr). Diese Klasse ist SCHEISSE. Bitte entschuldige das harte Wort, aber sie ist es. Denn sie ist auf HTML 4 stehen geblieben und schmeißt teils unsinnige Fehler bei ordentlichen HTML 5 Dokumenten. Noch schlimmer wird es, wenn Du im DOM manipulierst und daraus wieder ein HTML Dokument erzeugen willst.
Andere Libs wie der von Dir gefundene Parser von Gilles Paquette. Problem ist hier, dass die Integration immer nur mit Composer vorgesehen ist. Einerseits kein Wunder, Composer ist das Modulmanagementtool der PHP Welt, ähnlich NPM bei Node.js oder NUGET in der .net Welt. Aber man muss ihn halt installieren und verstehen, und man installiert ihn auf seinem Entwicklerrechner, NICHT auf dem Webserver, wo Du deine Seite hostest. Eine PHP Lib, für die man einfach ein ZIP herunterlädt und es in einen Ordner seines Webs entpackt, scheint es nicht zu geben. Keine Ahnung, ob man das per Composer irgendwie als Funktion zur Verfügung hat; wenn ich Composer sehe, winke ich immer gleich ab. Ich habe keine Lust, mich mit dem Ding zu befassen (bin aber auch kein beruflicher PHP Entwickler).
Deshalb - und nur deshalb - gebe ich Dir Tipps für Wege ohne Parser.
Rolf
@@Rolf B
!<p[ >]|</p>!
ist die Regex (regular expression).
Nein! RegEx(p) heißt nicht regular expression. (Ebensowenig wie charset für character set steht.)
Nicht mehr. Eben wegen solcher Erweiterungen wie du sie gezeigt hast.
Deshalb - und nur deshalb - gebe ich Dir Tipps für Wege ohne Parser.
Etwas anderes als einen Parser zum Parsen verwenden zu wollen ist problematisch.
😷 LLAP
Hallo Gunnar,
was soll die Dogmatik, wir sind hier nicht bei der Theoretischen Informatik. Die Dinger wurden so genannt und sind dann gewachsen, und nun heißen sie so. Auch wenn sie keine reguläre Sprache im Sinne der Theoretischen Informatik darstellen, sondern mehr sind. Deswegen würde sich Professor Chomsky nicht im Grab rumdrehen (unter anderem deshalb, weil er noch lebt).
Willst Du die Dinger umbenennen? Vielleicht Chamex (CHAracter Matching eXpression)? Geht nicht, ist schon reserviert. Strimax und Strimex sind auch schon vergeben.
Oder willst Du "Regexp" als Langform anders bezeichnen, z.B. "Regal Expulsion"[1]? Ist es für manche, aber hilft nicht beim Verständnis der Sache… Oder vielleicht doch? Wie war das noch mit den zwei Problemen 😉
Wie auch immer, auf diesem Haufen sitzen so viele Fliegen, die kriegst Du nicht davon weg.
Rolf
am besten wohl als "königlicher Scheiß" übersetzt ↩︎
@@Rolf B
was soll die Dogmatik
Die Dinge richtig zu benennen ist keine Dogmatik. Im Gegenteil ist die falsche Verwendung von Begriffen oft ein Ausdruck dafür, dass das Grundkonzept nicht verstanden wurde. So resultieren viele Probleme mit Zeichen daraus, dass der Unterschied zwischen Zeichensätzen und Zeichencodierungen nicht verstanden wurde.
Willst Du die Dinger umbenennen?
Man könnte einfach „(Such)muster“ sagen.
Oder willst Du "Regexp" als Langform anders bezeichnen, z.B. "Regal Expulsion" (am besten wohl als "königlicher Scheiß" übersetzt)?
Das gefällt mir.
😷 LLAP
@@noob-2000 (Auf dem Schlauch stehen Profi)
Gefühlt ist das "von hinten durch die Brust ins Auge" habt Ihr da eine bessere Idee?
Zunächst keine Idee, sondern eine Rückfrage.
Und was immer du vorhast, vermutlich ist nicht strpos
die Lösung, sondern $content
zu parsen.
😷 LLAP
Hallo Gunar,
erstmal vielen Dank für deine Signatur. Mein Kind ist 4 hat extreme Lungenprobleme, und kann aufgrund seines Gesundheitsstandes nicht (und der fehlenden Zulassung u.4 ) nicht geimpft werden. Aktuell ist mein Kinder wieder im Krankenhaus, ich würde mich sehr freuen wenn sich mehr Menschen impfen lassen.
Und was immer du vorhast, vermutlich ist nicht strpos die Lösung, sondern $content zu parsen.
Oh, das habe ich noch nie gemacht. Module wie https://github.com/paquettg/php-html-parser kann ich nicht nicht laden. Mir fehlen dazu die Rechte auf dem Hosting. Gibt es andere Parser?
@@Gunnar Bittersmann
… zu parsen.
Huch, wie bin ich denn in MDN und bei JavaScript gelandet? Hatte doch nach PHP gesucht?
DOMDocument::loadHTML sollte da eher die Stelle sein.
😷 LLAP
Tach!
Ich möchte in jedem 1/3 des Textes (nach dem </p> oder vor dem <p>)ein Javascript snippet einfügen.
Wenn sowieso Javascript laufen soll, würde ich vielleicht keine aufwendige Manipulation des HTML-Codes vornehmen, sondern eine des DOMs im Browser. Dort lassen sich die Elemente wesentlich einfacher finden und ändern.
dedlfix.