Jörg: Regulärer Ausdruck, Ausnahme einfügen

Hallo,

ich ersetze URLs, möchte aber youtube-Urls nicht darin haben.

Die Ausnahme gelingt mir nihct.

'!(((f|ht)tp(s)?://)[-a-zA-Z?-??-?()0-9@:%_+.~#?&;//=]+)!i'

Wie kann ich das machen?

Jörg

  1. Tach!

    ich ersetze URLs, möchte aber youtube-Urls nicht darin haben.

    Bevor du dir den Kopf zermarterst und einen unnötig komplexen Ausdruck fabrizierst, teste einfach in einem zweiten Schritt die extrahierte Domain per Stringvergleich. youtube.com allein reicht möglicherweise nicht, denn es gibt noch andere YT-Domains, die du vielleicht erkennen möchtest.

    dedlfix.

    1. Hallo dedlfix,

      hatte ich auch schon überlegt, aber

      ich ersetze URLs

      Wenn er preg_replace (oder mb_ereg_replace) macht, dann fehlt die Möglichkeit, im Replace-Handler zu sagen: den aber nicht.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hallo dedlfix, hallo Rolf,

        Wenn er preg_replace (oder mb_ereg_replace) macht, dann fehlt die Möglichkeit, im Replace-Handler zu sagen: den aber nicht.

        Genau, ich nutze preg_replace.

        Trotzdem kann ich dedlfix' Ansatz wählen.

        if(strpos($myText, 'youtube') === false) {
        preg_replace( ... );
        }
        

        Nachteil ist natürlich, dass jetzt jeglicher Text nicht mehr umgesetzt wird, falls irgendwo das Wort auftaucht.

        Danke und Gruß, Jörg

        1. Hallo Jörg,

          jo, und das ist nicht so schön.

          https://forum.selfhtml.org/self/2021/apr/19/wie-finde-ich-youtube

          würdest Du doch wohl finden wollen, gelle?

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hallo Jörg,

            jo, und das ist nicht so schön.

            https://forum.selfhtml.org/self/2021/apr/19/wie-finde-ich-youtube

            Hallo Rolf,

            nicht nur das, ich würde auch gerne dieses Thumbnail sehen, selbst wenn es youtube_thumb.jpg im Image-Tag heißt 😉

            Thumbnail

            Jörg

            1. Hallo Jörg,

              null problemo, die Bild-URL ist

              https://forum.selfhtml.org/images/5ae7eb18-a11c-11eb-b622-b42e9947ef30.jpg

              Rolf

              --
              sumpsi - posui - obstruxi
              1. Hallo Rolf,

                ok, Konjunktiv1 vergessen: ...ich würde auch gerne dieses Thumbnail sehen, selbst wenn es youtube_thumb.jpg im Image-Tag hieße.

                null problemo, die Bild-URL ist

                Dann nicht mehr.

                Zudem würde ich ja auch dieses Bild nicht sehen, da der Image-Tag von meinem preg_replace zerhackt wird. 😕

                Jörg

  2. Hallo Jörg,

    grundsätzlich würde ich vorschlagen, einen negativen Lookahead zu nutzen (sprich: die Regex matcht nicht, wenn der Lookahead matcht). Du müsstest nur festlegen, welche URLs der ausschließen soll. Spontan fallem mir 3 URLs für Youtube ein: youtube.com, youtube.de und youtu.be. Hinzu kommen natürlich noch massig nationale YT-URLs.

    Dedlfixens Ansatz müsste man, wenn ich länger drüber nachdenke, so implementieren, dass man zuerst einen Search macht, die URL prüft und nur dann, wenn es keine YT-URL ist, den Replace durchführt.

    Was ist das da überhaupt für eine Regex???

    '!(((f|ht)tp(s)?://)[-a-zA-Z?-??-?()0-9@:%_+.~#?&;//=]+)!i'

    Die ' sind Stringbegrenzer?
    Die ! sind Regex-Begrenzer?
    Demnach wäre das, case insensitive, dies:
    (((f|ht)tp(s)?://)[-a-zA-Z?-??-?()0-9@:%_+.~#?&;//=]+)

    Vorne ist das Schema, ok. Du akzeptierst ftp, http, ftps und https. Mööp. ftps gibt es nicht. Dann kommt ://, okay.

    Nun kommt eine Zeichenklasse. Ohne dessen Inhalt zu betrachten, steht da [...]+. Also 1-N Zeichen aus deiner Liste. Für ein URLy[1] ganz schön großzügig. URLs sind eigentlich viel strenger reglementiert. Aber na gut. Das ist ein anderes Thema.

    Deine Zeichenklassendefinition ist merkwürdig. Was sollen die ?-? Intervall? Wenn Du ein ? matchen willst - das hat innerhalb von [] keine Sonderrolle, das kannst Du einfach hinschreiben. Und was soll // ? Der Vorwärtsschrägstrich hat bei Dir keine Sonderrolle mehr, weil Du einen anderen Regex-Delimiter nimmst.

    Aber wenn man das alles ignoriert, wäre diese Regex machbar. Ich verteile sie auf 3 Zeilen, um besser erklären zu können. Du musst für Dich eine draus machen.

    ((ftp|https?)://
    (?!.*youtu(be\.de|be\.com|\.be)([:/]|$))
    [-a-zA-Z?()0-9@:%_+.~#?&;//=]+)
    

    Zeile 1 matcht das Schema. Dein (f|ht)tp(s)? Konstrukt habe ich so geändert, dass nur ftp, http und https gematcht wird. Für das optionale s braucht es keine Klammern. Es ist andersrum: du musst klammern, wenn das ? auf mehr wirken soll als nur das Zeichen links von sich.

    Die zweite Zeile ist der negative Lookahead. Der sieht so aus: (?!...), und das ... ist eine Regex, die ab diesem Punkt nicht matchen darf. Ich prüfe da auf [^/]*youtu(be\.com|be\.de|\.be), gefolgt von ([:/]|$).

    Das heißt: Auf eine Folge von Zeichen außer dem / folgt youtube.com, youtube.de oder youtu.be. Dahinter muss ein : oder / folgen, oder das Ende des Eingabestrings. Wenn diese Regex zutrifft, ist der negative Lookahead nicht erfüllt und die ganze Regex matcht nicht.

    Die dritte Zeile ist der domain/path/query Matcher, den Du vorher schon hattest, ohne die ?-??-? Sequenz.

    https://regex101.com/ sagt: Müsste funktionieren.

    Rolf

    --
    sumpsi - posui - obstruxi

    1. Man weiß ja nicht, ob es der oder die URL ist. Also entgendern wir das zu das URLy 😂 ↩︎

    1. Hallo Rolf,

      habe mir jetzt lange Zeit genommen, um das zu verstehen, kann aber nicht behaupten, dass es mir gelungen wäre 😉

      Funktionieren tut es sehr gut, soweit ich das testen konnte. Insofern schonmal danke für die Erklärung und die Mühe.

      Ich kann Dir auch nicht erklären, was welche Stelle in miener Regex macht oder machen soll.

      Hintergrund war, dass ich irgerndwann mal wollte, dass alle Links über ein dereferer-script laufen, daher war die komplette regex:

      $text = preg_replace('!(((f|ht)tp(s)?://)[-a-zA-Z?-??-?()0-9@:%_+.~#?&;//=]+)!i','<a href=\"dereferscript.php?target1=$1\" target=\"_blank\">$1</a>',$text);
      

      Nun stehe ich aber immer noch vor dem Problem, hier sowohl youtube.* als auch die Verwendung einer URL in einem Image-Tag heraus zu nehmen 😕

      Jörg

      1. Hallo Jörg,

        für das, was die von Dir gezeigte Codezeile tut, scheint mir ein regex-Replace eine sehr ungeeignete Lösung zu sein.

        Und dieser Satz

        Ich kann Dir auch nicht erklären, was welche Stelle in miener Regex macht oder machen soll.

        ärgert mich. Es ist also nicht deine Regex. Du hast sie irgendwoher kopiert. Vermutlich zusammen mit dem Replace-Ausdruck. Letztendlich geht es Dir doch darum, aus $text einen Link zu machen, falls in $text eine URL steht.

        Eine bessere Lösung wäre diese Funktion, die ich als Pseudocode aufgeschrieben habe. PHP daraus machen ist deine Hausaufgabe 😉. Eine Funktion deshalb, damit man Schritt für Schritt die erforderlichen Prüfungen ausführen kann und aussteigt, wenn eine fehlschlägt. Das vermeidet ein Einrückungsgebirge.

        function create_link_for_text($text)
        {
           if (!($text ist gültige URL))
              return $text;
        
           $teile = parse-url($text);
           if (!(schema ist zulässig))
              return $text;
           if (host enthält youtube)
              return $text;
        
           $url = urlencode($text);
           return "<a href='dereferscript.php?target1=" . 
                  urlencode($text) . 
                  "' target='_blank'>" . 
                  htmlspecialchars($text) . 
                  "</a>";
        }
        

        Die Prüfung, ob $text eine gültige URL enthält, gelingt mit filter_var($text, FILTER_VALIDATE_URL). Allerdings lässt das auch Schemata zu, die Du nicht willst.

        Deswegen folgt als nächstes die Zerlegung der URL in ihre Teile, dafür hat PHP die Funktion parse_url. Die liefert Dir ein assoziatives Array mit den Einzelteilen der URL, aber sie validiert nicht, ob Du ihr eine gültige URL gibst. Deswegen vorher die Prüfung mit filter_var.

        Das $teile-Array hat Schlüssel für die einzelnen URL-Teile. Deren Namen stecken in PHP-Konstanten; du brauchst PHP_URL_SCHEME und PHP_URL_HOST. Damit musst Du prüfen, ob das Schema ftp, http oder https ist. Wenn nicht - kein Link. Und dann musst Du prüfen, ob der Host auf youtube.de, youtube.com oder youtu.be endet. Wenn ja, kein Link.

        Ist alles gelungen, folgen zwei Dinge, die Du vergessen hattest: die URL, die Du übergeben willst, muss mit urlencode codiert werden. Und der Linktext muss mit htmlspecialchars behandelt werden.

        Das Ergebnis der Funktion ist dann entweder das HTML Fragment mit dem Link, oder der unveränderte Eingabetext.

        Wie man filter_var und parse_url einbaut, findest Du sicher selbst heraus. Die Dokumentationsseiten habe ich Dir verlinkt - Dokulesen gehört zwar zu den okkulten Künsten, aber es lohnt, diese Kunst zu erlernen. Und dann weißt Du auch, was die Funktion tut und brauchst keine rätselhaften Regechsen zu jagen.

        Das Ergebnis ist dann zwar kein Einzeiler mehr. Aber das macht nichts. Einzeiler sind nicht zwangsläufig die bessere Lösung. Dieser Mehrzeiler ist besser lesbar und tut vor allem viel mehr, und sinnvolleres.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          ärgert mich.

          Sorry, war nicht meine Absicht. Umso netter, dass Du mir trotzdem hilfst.

          Letztendlich geht es Dir doch darum, aus $text einen Link zu machen, falls in $text eine URL steht.

          Ja, fast. Es geht mir darum, alle URLs in $text zu finden und daraus Links zu machen, die aber nicht direkt auf das Linkziel, sondern über mein Refererscript dorthin führen.

          Könnte man ja über preg_match_all machen. Dann kommt aber dazu, dass ich die Links, die in Image-Tags stecken, nicht umwandeln möchte. Das macht mir grad ein bischen Kopfzerbrechen. 😕

          Eine bessere Lösung wäre diese Funktion, die ich als Pseudocode aufgeschrieben habe. PHP daraus machen ist deine Hausaufgabe 😉.

          Das ist ok. Aber so, wie es aussieht, komme ich mit dem Pseudocode noch nicht ganz hin.

          Das Ergebnis ist dann zwar kein Einzeiler mehr. Aber das macht nichts. Einzeiler sind nicht zwangsläufig die bessere Lösung. Dieser Mehrzeiler ist besser lesbar und tut vor allem viel mehr, und sinnvolleres.

          Das wäre undramatisch, da die Nutzung der Funktion ja ein Einzeiler bleiben wird.

          Bleiben die beiden Fragen:

          1: preg_match_all nutzen? 2: Was tun gegen (bzw. für) die Links, die in Image-Tags stecken?

          Jörg

          1. Hallo Jörg,

            alle URLs in $text

            aha, so langsam kommmen die Anforderungen an das Konstrukt ans Tageslicht 😉

            da die Nutzung der Funktion ja ein Einzeiler bleiben wird.

            Wetten, dass das nicht so sein wird? Wenn du nämlich

            Links, die in Image-Tags stecken

            hast, dann wäre mal die nächste Frage: Wo denn noch? Worum handelt es sich bei $text?

            Welche URLs willst Du konvertieren, und welche nicht? Kannst Du das genau spezifizieren, außer mit einem vagen Fingerzeig?

            Deine Konvertierung ist kontextabhängig, und den wirst Du mit einem preg_match_all niemals finden.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              alle URLs in $text

              aha, so langsam kommmen die Anforderungen an das Konstrukt ans Tageslicht 😉

              Ich dachte, das hätte ich schon irgendwo erwähnt gehabt 😏

              dann wäre mal die nächste Frage: Wo denn noch? Worum handelt es sich bei $text?

              Welche URLs willst Du konvertieren, und welche nicht? Kannst Du das genau spezifizieren, außer mit einem vagen Fingerzeig?

              Es sind eigentlich 2 Stellen. Einmal in einem Adressfooter, der sowohl die Webadresse als auch die Email als auch ein Image des Users beinhalten kann. Aber einmal auch als Freitext, ähnlich eines Foreneintrages.

              Die Emailadresse stellt hierbei das kleinere Problem dar, da ich das auch über tinymce machen kann. Aber die Umleitung der Links über ein derefererscript kann ich darüber nich steuern. Daher muss ich die Links finden. Zugleich wäre natürlich schön, wenn ich Videos einbetten und Images erlauben könnte und die dann eben auch nicht umgewandelt werden.

              Das ist der Hintergrund der Geschichte.

              Jörg

              Deine Konvertierung ist kontextabhängig, und den wirst Du mit einem preg_match_all niemals finden.

              Rolf

              1. Hallo Jörg,

                irgendwo

                Nena, 1984... - seit „Dark“ mein Horror-Song 😉

                Ich bin 56. Glaubst Du etwa, ich wäre im Stande, mich an eine Irgendwo-Erwähnung auch nur ansatzweise zu erinnern?

                Einmal in einem Adressfooter, der sowohl die Webadresse als auch die Email als auch ein Image des Users beinhalten kann. Aber einmal auch als Freitext, ähnlich eines Foreneintrages.

                D.h. das sind Eingabefelder?

                (1) Der er User kann seinen „Adress-Footer“ eintragen, so wie eine Signatur, und darin ist HTML möglich? Dann musst Du aber zwischen <img src="..."> und <a href="..."> unterscheiden, denn du möchtest Links ja dereferieren.

                (2) Und es gibt ein Freitextfeld, wo man URLs eintragen kann. Und die möchtest Du automatisch zum Link machen. Aber eben nur die URLs, die nicht in einem img Tag stehen. D.h. in deinem Freitext ist HTML erlaubt und dieses HTML wird auch als HTML wieder zum Brauser geschickt? Brrr - Red Alert - ich hoffe, dieses HTML bekommt nur der User zu sehen, der es selbst eingegeben hat, denn andernfalls solltest Du dieses Feature schnellstens entfernen. User-HTML ist per Definition nicht vertrauenswürdig. Du könntest natürlich nur bestimmte HTML Elemente zulassen. Ich traue Dir angesichts des Gesprächsverlaufs aber nicht zu, das korrekt umzusetzen.

                Wenn User auf einer Seite URLs hinterlassen können, die beim Abruf der Seite automatisch wirksam werden, ist Schabernack in jeder beliebigen Menge möglich. Wenn die User auch noch beliebiges HTML hinterlassen können, ist deine Seite lebensgefährlich. Ich sag nur: <script>.

                Wenn Du in deinen User-Eingaben die Zeichen <, > und & findest, dann musst Du sie durch &lt;, &gt; und &amp; ersetzen. Kein User-HTML, niemals. Wenn der User ein Bild in seinen Footer einbauen will, erschaffe eine eigene Syntax. Das tut unser Forum auch. Zum Beispiel: @http://example.org/foo/bar/bild2.jpg. Die Bild-URL musst Du prüfen, ob sie auf jpg, jpeg, gif, png oder svg endet. Das ist zwar bei manchen Bildquellen blöd, verhindert aber URLs für vermeintliche Bilder wie http://example.org/admin/user/843749?delete - was bei einem richtig schlecht gebauten Forum eben mal einen User töten könnte.

                Für Bilder und Links kannst Du dann dein Verfahren anwenden, sie aus dem Text herauszusuchen und ein img oder a Element daraus zu machen. Diesem a Element gibst Du das rel Attribut: rel="nofollow noopener noreferrer". Dann brauchst Du auch kein dereferer-Script mehr. Das rel-Attribut erledigt das.

                Ein preg-replace könnte das sogar hinbekommen. Aber dann mit Callback, und im Callback nimmst Du die nötigen Prüfungen vor.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo Rolf,

                  Wenn User auf einer Seite URLs hinterlassen können, die beim Abruf der Seite automatisch wirksam werden, ist Schabernack in jeder beliebigen Menge möglich. Wenn die User auch noch beliebiges HTML hinterlassen können, ist deine Seite lebensgefährlich. Ich sag nur: <script>.

                  Wenn Du in deinen User-Eingaben die Zeichen <, > und & findest, dann musst Du sie durch &lt;, &gt; und &amp; ersetzen. Kein User-HTML, niemals. Wenn der User ein Bild in seinen Footer einbauen will, erschaffe eine eigene Syntax.

                  Halt, stop, wer sagt denn Sowas? <, > werden bisher jetzt schon umgewandelt und der einzige User, der Links (außer seiner eigenen, mir bekannten Homepageadresse) hinterlassen kann, bin ich selber. Meine eigenen Links laufen über eine Art "BB-Code", die ich dann abhängig vom Adressaten in Links oder Entsprechendes wandle.

                  Soweit bisher.

                  Ich wollte nun Links für User freigeben und die sollen über ein Refererscript gehen. Und ich dachte daran, Bilder frei zu geben.

                  Was ist daran verwerflich oder gefährlich? Von HTML freigeben war nie die Rede.

                  Jörg

                  1. Hallo Jörg,

                    okay. Das ist immer so eine Sache mit den Glaskugeln - man glaubt was zu sehen und ist lieber zu vorsichtig.

                    Wenn Du schon einen BBCode hast, dann nutz ihn doch für deine Freigaben. Die User sollen ja sicherlich keine <a> oder <img> Elemente erstellen. Damit ist der Kontext einfacher und Du musst keine beliebigen URLs in freier Wildbahn finden.

                    Das geht sehr gut mit preg_replace_callback. Die Regex sollte dann nur den BBCode erkennen, und die Prüfung auf eine valide URL machst Du in der Callback-Funktion.

                    Wenn Du diese BBCodes akzeptierst:

                    [a]url[/a]
                    [a="url"]text[/a]
                    [img]url[/img]

                    ist das fix erledigt.

                    Um matchende Paare im BBCode zu erkennen, empfehle ich Dir Backreferences. Beispiel:

                    \[([a-z]+)].*?[/\1]  
                      ********      ^^ 
                    

                    findet alle Paare von [tag]...[/tag], egal welches Tag (solange man davon ausgeht, dass innerhalb von BBCode-Tags kein BBCode mehr erlaubt ist)

                    Über den Sternchen steht ein ganz normaler Teil der Regex. Hier: [a-z]+. Das ist eingeklammert, weil Backreferences sich immer auf Klammern beziehen. Es ist die erste Klammer, darum ist die Backreference \1. Die wird an der mit ^^ markierten Stelle benutzt. Die Regex Engine sucht also an dieser Stelle das, was sie vorher im Klammerpaar Nr. 1 gefunden hat.

                    Wenn Du das öffnende Tag noch um einen optionalen Parameter erweiterst:

                    \[([a-z]+)(=".?*")?]

                    Und noch ein paar benannte Gruppen daraus machst:

                    \[(?<tag>[a-z]+)(="(?<url>.?*)")?](?<content>.*?)[/\1]

                    Dann bekommst Du im Callback von preg_replace_callback ein Array, in dem die Schlüssel tag, url und content stehen. Die kannst Du dann auswerten, kannst prüfen, ob es eine valide URL ist, kannst das Schema prüfen (wie heute nachmittag erzählt) und dann, wenn Du einen Link oder ein Bild erzeugen willst, einen entsprechenden Replace-String zurückgeben.

                    Details findest Du in der PHP-Doku, das kau ich Dir jetzt nicht vor. Aber ich hab's gerade testweise gebaut, das geht.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                    1. Hallo Rolf,

                      Wenn Du schon einen BBCode hast, dann nutz ihn doch für deine Freigaben. Die User sollen ja sicherlich keine <a> oder <img> Elemente erstellen. Damit ist der Kontext einfacher und Du musst keine beliebigen URLs in freier Wildbahn finden.

                      Ich bin gerade im begriff, den tinymce-Editor raus zu schmeißen, da dieser nicht alle Elemente, die ich schön fände (z.b. Font, Fontcolor, usw.) in BBcode umsetzt. Die wichtigen, ja, aber eben nicht alle. Zudem ist er ziemlich schlecht, wenn der User formatierten Text in die Textarea einfügt. Da habe ich nun einen besseren Editor gefunden, der diese Umsetzung besser hin bekommt.

                      Das geht sehr gut mit preg_replace_callback. Die Regex sollte dann nur den BBCode erkennen, und die Prüfung auf eine valide URL machst Du in der Callback-Funktion.

                      Und hier habe ich dann das nächste Problem. Gerade, weil der neue Editor so viel BBcode produzieren kann, der mir gut gefällt, habe ich beschlossen, vom selben Autor ein, wenn auch ältere BBcode2html-Klasse für die Ausgabe des BBcode zu nutzen. Hiermit würde ich übrigens auch mein Ursprungsproblem erschlagen. (Ich hoffe, dass die Klasse unter php 7.4 auch noch läuft.)

                      Leider gelingt es mir nicht, diese Klasse zu nutzen und ich habe keinen Schimmer, was ich falsch mache.

                      require('./bbcode/SBBCodeParser.php');
                      $parser = new SBBCodeParser_Document();
                      

                      ergibt

                      Fatal error: Uncaught Error: Class 'SBBCodeParser_Document' not found in ...
                      

                      Wenn ich diese Fehlermeldung korrekt interpretiere, bedeutet das ja, das Script wurde eingebunden, aber die Klasse gibts gar nicht.

                      Hast Du mal eien Tip da, was ich hier falsch mache?

                      Oder sollte ich eh eine neuere Klasse suchen? Bin aber leider mit dem Composer nicht fit. 😟

                      Jörg

                      1. Hallo Jörg,

                        $parser = new SBBCodeParser_Document();

                        Wo hast Du diese Zeile her? Sieht nach einem Fantasiekonstrukt aus. Oder Du hast eine andere Doku gefunden als ich.

                        So wie ich das auf der Github-Seite sehe, befindet sich der SBBCodeParser in einem namespace, du musst also entweder mit dem use-Statement den Namespace importieren, oder die Klassen vollqualifiziert ansprechen. Und dann den Klassennamen verwenden, den Sam benutzt hat.

                        Die Beispiele bei Sam Clarke zeigen die vollqualifizierte Version.

                        $parser = new \SBBCodeParser\Node_Container_Document();
                        

                        SBBCodeParser ist der Namespace, Node_Container_Document ist die Klasse.

                        Und dann noch dies: hast Du nur die SBBCodeParser-Datei heruntergeladen? Oder auch den vollständigen classes-Unterordner? Im Gegensatz zu vielen anderen bietet Sam keine Composer-Unterstützung für die Installation und auch kein Zip mit allen Dateien drin, das macht es etwas mühseliger. Entweder musst Du das Repository lokal klonen, oder die Dateien einzeln herunterladen.

                        Und dann sollte das auch funktionieren.

                        Rolf

                        --
                        sumpsi - posui - obstruxi
                        1. Hallo Rolf,

                          was würde ich nur ohne Dich machen? 😉 Vielen, vielen Dank!

                          $parser = new SBBCodeParser_Document();

                          Wo hast Du diese Zeile her? Sieht nach einem Fantasiekonstrukt aus. Oder Du hast eine andere Doku gefunden als ich.

                          Ich hatte die Anleitung auf der Homepage des Autors genommen. Das war aber weniger das Problem, als das, dass ich mich mit Namespace un dessen Verwendung nicht auskenne. Daher danke für Deine Erklärung!

                          Nun funktioniert es.

                          Und dann noch dies: hast Du nur die SBBCodeParser-Datei heruntergeladen? Oder auch den vollständigen classes-Unterordner? Im Gegensatz zu vielen anderen bietet Sam keine Composer-Unterstützung für die Installation und auch kein Zip mit allen Dateien drin, das macht es etwas mühseliger. Entweder musst Du das Repository lokal klonen, oder die Dateien einzeln herunterladen.

                          Man kann in Github aber das komplette .zip-file herunter laden, insofern passt jetzt auch alles und die Klasse wird genutzt.

                          Danke nochmal 👍, hätte ich alleine nicht oder nur durch Zufall hin bekommen.

                          Jörg

                          1. Hallo Jörg,

                            Ich hatte die Anleitung auf der Homepage des Autors genommen.

                            Siehste, die hab ich gar nicht gefunden. 2011 - puh, gut das du schon mal da warst und den ärgsten Staub weggepustet hattest. Offenbar hat Sam seitdem auf Namespaces umgestellt und die Klassennamen geändert. Aber Doku anpassen? Nööö, wozu, steht doch alles bei Github...

                            Rolf

                            --
                            sumpsi - posui - obstruxi
                            1. Hallo Rolf,

                              Siehste, die hab ich gar nicht gefunden. 2011 - puh, gut das du schon mal da warst und den ärgsten Staub weggepustet hattest. Offenbar hat Sam seitdem auf Namespaces umgestellt und die Klassennamen geändert. Aber Doku anpassen? Nööö, wozu, steht doch alles bei Github...

                              Ich bin auch (leider) nicht wirklich zufrieden mit dem Ergebnis 😟

                              Die rote Font-Farbe wird gar niocht umgesetzt und der Autor geht doch sehr großzügig mit br-Tags um (und ich finde nicht, wo ich sie eliminieren kann)😕

                              <div style="text-align: center"><span style="font-size: medium"><strong>BBCode SCEditor</strong></span></div><br />
                              <br />
                              <br />
                              <br />
                              Give it a try! <img alt=":)" src="http://localhost/Classes/SCEditor-punbb/punbb-1.3.5/img/smilies/smile.png" /><br />
                              <br />
                              <br />
                              <br />
                              <span style="color: #ff00">Red text! </span><span style="color: #3399ff">Blue?</span><br />
                              <br />
                              <br />
                              <br />
                              <ul><br />
                              <br />
                              <li>A simple list</li><br />
                              <br />
                              <li>list item 2</li><br />
                              <br />
                              </ul><br />
                              <br />
                              <br />
                              <br />
                              If you are using IE9+ or any non-IE browser just type <strong>:</strong>) and it should be converted into <img alt=":)" src="http://localhost/Classes/SCEditor-punbb/punbb-1.3.5/img/smilies/smile.png" /> as you type.<br />
                              <br />
                              <br />
                              <br />
                              <ul><br />
                              <br />
                              <li>test</li><br />
                              <br />
                              <li>test2</li><br />
                              <br />
                              <li>test3</li><br />
                              <br />
                              <li>test4</li><br />
                              <br />
                              </ul><br />
                              <br />
                              <br />
                              <br />
                              

                              Jörg

                              1. Hallo Jörg,

                                tja, da werde ich Dir jetzt nicht helfen können - dafür müsste ich ein eigenes Testprojekt aufsetzen. Das ist mir zu viel Arbeit. Ist dein Projekt irgendwo online?

                                Rolf

                                --
                                sumpsi - posui - obstruxi
                                1. Hallo Rolf,

                                  tja, da werde ich Dir jetzt nicht helfen können - dafür müsste ich ein eigenes Testprojekt aufsetzen. Das ist mir zu viel Arbeit. Ist dein Projekt irgendwo online?

                                  Das verstehe ich gut. Und nein, ist nicht online.

                                  Was ich nur nicht verstehe:

                                  Aus diesem BBcode:

                                  [center][size=3][b]BBCode SCEditor[/b][/size][/center]
                                  
                                  Give it a try! :)
                                  
                                  [color=#ff00]Red text! [/color][color=#3399ff]Blue?[/color]
                                  
                                  [ul]
                                  [li]A simple list[/li]
                                  [li]list item 2[/li]
                                  [/ul]
                                  
                                  If you are using IE9+ or any non-IE browser just type [b]:[/b]) and it should be converted into :) as you type.
                                  
                                  [ul]
                                  [li]test[/li]
                                  [li]test2[/li]
                                  [li]test3[/li]
                                  [li]test4[/li]
                                  [/ul]
                                  

                                  generiert die Klasse obigen html-Code.

                                  Und bei meiner Durchsuche nach br-Tags finde ich im kompletten Ordner (inkl. Unterordner) nur eine einzige Zeile, in der ein br-Tag überhaupt vorkommt, nämlich

                                  new BBCode('br', '<br />', BBCode::INLINE_TAG, true),
                                  

                                  Diese scheint mir aber gar nicht ursächlich für die vielen br-Tags, denn selbst wenn ich die Zeile entferne, bleiben mächtig viele <br> übrig.

                                  Und ich finde keinen Hinweis im Script, wo die her kommen.

                                  Jörg

                                  Edit: Ok, da scheint es eine Option zu geben, ob man auf jede neue Zeile nl2br anwendet:

                                  	public function get_html($nl2br=true)
                                  	{
                                  		if(!$nl2br)
                                  			return str_replace("  ", " &nbsp;", htmlentities($this->text, ENT_QUOTES | ENT_IGNORE, "UTF-8"));
                                  
                                  		return str_replace("  ", " &nbsp;", nl2br(htmlentities($this->text, ENT_QUOTES | ENT_IGNORE, "UTF-8")));
                                  	}
                                  

                                  Damit wird es klarer.

                                  1. Damit wird es klarer.

                                    Das Blöde ist nur, dass diese Zeile:

                                    return str_replace("  ", " &nbsp;", nl2br(htmlentities($this->text, ENT_QUOTES | ENT_IGNORE, "UTF-8")));
                                    

                                    entweder gar keinen Zeilenumbruch macht, wenn ich den Parameter auf 'false* setze oder dann gleich 2 oder 3. 😕

                                    1. Hallo Jörg,

                                      nl2br konvertiert - in der PHP Sandbox - \r, \n und \r\n in jeweils ein <br>. Wenn sie \n\r antrifft, erzeugt sie <br> und einen Zeilenumbruch.

                                      D.h. wenn sie bei Dir einen Schwarm von <br> erzeugt, wo nur eins sein sollte, dann sind auch so viele Zeilenende-Sequenzen da. Wo die herkommen, ist natürlich eine gute Frage, das mag auch an der Arbeitsweise der Lib liegen. Keine Ahnung.

                                      Rolf

                                      --
                                      sumpsi - posui - obstruxi
                                      1. Hallo Rolf,

                                        D.h. wenn sie bei Dir einen Schwarm von <br> erzeugt, wo nur eins sein sollte, dann sind auch so viele Zeilenende-Sequenzen da. Wo die herkommen, ist natürlich eine gute Frage, das mag auch an der Arbeitsweise der Lib liegen. Keine Ahnung.

                                        Das ist ja nicht alles. Denn auch wenn nur ein <br> je Zeilenende erzeugt werden würde, habe ich ja noch viele, viele <br>, die hinter divs, tables, tr, td, ul, li, usw. erzeuigt werden, die zwecks sinnvoller Verschachtelung Zeilenumbrüche beinhalten.

                                        Das scheint ein ganz grundsätzliches Problem beim parsen von BBcode zu sein, wie ich heute erruieren konnte.

                                        Ich müsste also meinen inhalt des Textarrays anhand der Zeilenumbrüche explodieren und mir jedes Zeilenende anschauen. Oder mal wieder mit einem regulären Ausdruck heran gehen. 🤣

                                        Jörg

    2. Hallo Rolf,

      Aber wenn man das alles ignoriert, wäre diese Regex machbar. Ich verteile sie auf 3 Zeilen, um besser erklären zu können. Du musst für Dich eine draus machen.

      ((ftp|https?)://
      (?!.*youtu(be\.de|be\.com|\.be)([:/]|$))
      [-a-zA-Z?()0-9@:%_+.~#?&;//=]+)
      

      Die Regex erkennt also URLs, das passt also schonmal.

      Aber ich habe das System einer Regex nicht verstanden, das aus
      http://www.example.com
      ./myscript.php?target=http://www.example.com macht.

      Wie geht sowas?

      Jörg

      1. Hallo Jörg,

        die Replace-Methode verwendet $0, um den kompletten Match zu repräsentieren, und $1 bis $99 für die Gruppen im Match (sogenannte capturing groups). Jedes Klammerpaar, das Du in der Regex hast, bildet eine capturing group (es sei denn, du bildest explizit eine non-capturing group, mit (?:...) ).

        Die Regex, über die wir bisher gesprochen haben, dient nur dazu, youtube-URLs auszublenden. Sie unterscheidet aber (noch) nicht zwischen dem Domain-Teil der URL und dem nachgelagerten Pfad zur Datei. Brauchst Du das? Was ist mit der URL http://www.example.com/foo/bar.html?bin=go - welcher Teil davon soll im Target-Parameter aufkreuzen?

        Im einfachen Fall, wenn Du also die komplette URL als target angeben willst, kannst Du $0 im Replace-Ausdruck verwenden. Allerdings nicht ganz blindlings, denn URLs haben Regeln und du musst die Zeichen der Ausgangs-URL ggf. codieren, um die als Parameter einer anderen URL angeben zu können. Ich meine, ich hätte schon mal mit Hilfe eines Replace-Callbacks gezeigt, wie das geht.

        $0 ist der komplette Match. Wenn Du nur einen Teil des Matches brauchst, dann müssen wir schauen, ob dieser Teil überhaupt als Gruppe vorhanden ist, oder ob man dafür noch eine passende Gruppe bilden muss.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          die Replace-Methode verwendet $0, um den kompletten Match zu repräsentieren, und $1 bis $99 für die Gruppen im Match (sogenannte capturing groups). Jedes Klammerpaar, das Du in der Regex hast, bildet eine capturing group (es sei denn, du bildest explizit eine non-capturing group, mit (?:...) ).

          Ok. Das wußte ich nicht.

          Die Regex, über die wir bisher gesprochen haben, dient nur dazu, youtube-URLs auszublenden. Sie unterscheidet aber (noch) nicht zwischen dem Domain-Teil der URL und dem nachgelagerten Pfad zur Datei. Brauchst Du das? Was ist mit der URL http://www.example.com/foo/bar.html?bin=go - welcher Teil davon soll im Target-Parameter aufkreuzen?

          Eigentlich der komplette Teil. Im Prinzip geht es darum, aus diesem oder einem ähnlichem Block die href-Attribute auszutauschen, die Url vor dem schließenden a-Tag soll natürlich erhalten bleiben. (Du erinnerst Dich an die Regex, die nicht von mir war. Die hat das ganz gut gemacht, passt aber nicht mehr, außerdem hab ich die ja eh nie verstanden 😉 )

          Test
          <a href="http://www.example.com">www.example.com</a><br />
          <a href="https://www.example.com">https://www.example.com</a><br />
          <a href="http://example.com">http://example.com</a><br />
          testing
          

          Im einfachen Fall, wenn Du also die komplette URL als target angeben willst, kannst Du $0 im Replace-Ausdruck verwenden. Allerdings nicht ganz blindlings, denn URLs haben Regeln und du musst die Zeichen der Ausgangs-URL ggf. codieren, um die als Parameter einer anderen URL angeben zu können. Ich meine, ich hätte schon mal mit Hilfe eines Replace-Callbacks gezeigt, wie das geht.

          Kann m ich grad nicht erinnern. Meinst Du, dass aus z.b. & ein &amp; werden muss?

          $0 ist der komplette Match. Wenn Du nur einen Teil des Matches brauchst, dann müssen wir schauen, ob dieser Teil überhaupt als Gruppe vorhanden ist, oder ob man dafür noch eine passende Gruppe bilden muss.

          Ich glaube, der komplette Match wäre ok. Aber ich krieg die Regex nicht hin, auch nicht mit Deinem Grundkonstrukt. 😕

          Jörg

          1. Hallo Jörg,

            Meinst Du, dass aus z.b. & ein &amp; werden muss?

            Nein. Man muss für den passenden Kontext maskieren. &amp; ist für HTML, du brauchst aber die Maskierung für ein URL. Oder, um ein Beispiel aus dem wirklichen Leben zu bringen: Für Karneval maskierst Du Dich auch anders als für deine Banküberfälle, nicht wahr?

            In einer URL nimmt man für Bytes, die zu maskieren sind, ihren Codewert. Der Doppelpunkt hat Codewert 58 (hex: \x3A). Der Schrägstrich die 47 (hex: \x2F). Ein ä - tjaaa, hier scheiden sich die Geister, aber man empfiehlt, die Zeichen jenseits des Codes 127 durch UTF-8 zu codieren. Ein ä wäre demnach durch die Bytes \xc3 \xa4 darzustellen. Der hexadezimalen Darstellung des Codewertes wird ein % Zeichen vorangestellt. Ein : ist also %3A, ein / ist %2F, ein ä ist %C3%A4.

            Ich müsste das im Detail auch nachgucken, was wo erlaubt ist, aber man ist auf der sicheren Seite, wenn man alles, was kein Buchstabe und keine Zahl ist, maskiert.

            http://example.com müsstest Du also, um sicher zu sein, so übergeben:

            ./myscript.php?target=http%3A%2F%2Fwww.example.com

            Den Punkt kannst Du lassen.

            Das Schöne an der Sache ist: PHP nimmt Dir die Arbeit ab. Es gibt die Funktion urlencode. Guck in die Doku dafür.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hi Rolf,

              Den Punkt kannst Du lassen.

              Das Schöne an der Sache ist: PHP nimmt Dir die Arbeit ab. Es gibt die Funktion urlencode. Guck in die Doku dafür.

              Mach ich. Aber soweit bion ich noch gar nicht. Meine Regex matcht noch nicht 😕

              Guten Start in die Woche

              Jörg

  3. Script:

    <?php
    
    // Text einlesen
    $text = "[size=3][b]BBCode SCEditor[/b][/size]
    
    Give it a try! :)
    
    [color=#ff00]Red text! [/color][color=#3399ff]Blue?[/color]
    
    [ul]
    [li]A simple list[/li]
    [li]list item 2[/li]
    [/ul]
    
    If you are u[font=Comic Sans MS]sing IE9+[/font] or any non-IE browser just type [b]:[/b]) and it should be heriuhg3r  www.test.de oder aber auch [url=test.de]test.de[/url] converted into :) as you type.
    
    [table][tr][td]wrtz4z[/td]
    [td][/td]
    [/tr]
    [tr][td][/td]
    [td]dzerh[/td]
    [/tr]
    [/table]";
    
    # Zeilenumbrüche korrigieren
    $text = str_replace("\r\n","\n",$text); # windows -> linux
    $text = str_replace("\r","\n",$text); # mac -> linux
    
    # array bilden
    $arr_text = explode("\n",$text);
    $neuerText = '';
    
    foreach($arr_text as $zeile) {
    
        if (preg_match("/\[\/?(li|ul|td|tr|table|code|quote|list|ol)\]$/",$zeile)) {
            $zeile = str_replace("\n", "", $zeile);
           echo $zeile;
        } else {
            echo $zeile."<br>";
        }
    
       $neuerText .= $zeile;
    
    }
    
    echo "<hr>".nl2br($neuerText);
    

    Ergebnis:

    Jörg

    1. Hallo Jörg,

      so langsam komme ich dahinter, was du treibst und wo genau deine Probleme sind 😉

      Du entfernst durch den explode die \n aus der Eingabe. In der Schleife setzt du $neuerText ohne \n wieder zusammen.

      Ergebnis: der alte Text ohne jeden Zeilenumbruch.

      Dein str_relace innerhalb der Schleife ist sinnlos, denke ich. Nach dem explode sind in den Trümmerstücken keine \n mehr zu finden, die sind alle explodiert. Der str_replace wird niemals etwas zu tun bekommen.

      Geht es Dir darum, Zeilenumbrüche zu beseitigen, wenn zwei BBCodes aufeinander folgen und zwischen ihnen nur Whitespace ist?

      Also z.B. an Stellen wie hier:

      [tr][td][/td]
      [td]dzerh[/td]
      [/tr]
      

      Der BBCode-Konverter sollte daraus dies machen:

      <tr><td></td>
      <td>dzerh</td>
      </tr>
      

      und das ist soweit okay. Aber dein nl2br, mit dem Du in normalem Text Zeilenumbrüche erhalten willst, schießt Dir quer und setzt da <br> ein. Das dürfte das Problem sein.

      Im ersten Schritt könnte man versuchen, nur dann ein <br> einzusetzen, wenn man zwei Zeilenumbrüche hintereinander findet. Dazwischen kann noch Whitespace sein, es ist nicht selten, dass jemand Leerstellen in einer Leerzeile hat. Du könntest also statt nl2br einen Replace von /\n\s*\n/ durch <br><br> ausprobieren. Dein System würde sich dann ähnlich wie das Forum verhalten: wenn Du keine Leerzeile machst, wird der Text zu einem Paragraphen zusammengepappt.

      Die CForum-Sonderlocke, dass ein Zeilenumbruch erhalten bleibt, wenn eine Zeile auf zwei Leerstellen endet, so wie hier,
      kannst Du ggf. auch noch irgendwie realisieren, wenn Du zuerst /(\S) {2,}\n/ durch $1<br> ersetzt. Diese Regex matcht ein Non-Whitespace, zwei oder mehr Spaces und ein \n. In der Ersetzung wird das gematchte Non-Whitespace behalten und dann ein <br> gesetzt. Man könnte ggf. noch das geschützte Space berücksichtigen (\u00a0), aber nicht \s (weil das auch \n matcht).

      Das "Zusammenpappen zu einem Paragraphen" würde ich dann aber den Browser überlassen. Ich denke, es kann zu unerwünschten Nebenwirkungen führen, wenn Du blindlings \n durch " " ersetzt.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hallo Rolf,

        so langsam komme ich dahinter, was du treibst und wo genau deine Probleme sind 😉

        Ich wurschtel schon viel zu lange daran herum, finde ich. Aber so ist das manchmal mit anfangs so klein erscheinenden Problemen, die es im weoiteren verlauf aber dann doch ganz schön "in sich" haben.

        Du entfernst durch den explode die \n aus der Eingabe.

        Ach herrje, natürlich. Das erklärt und beantwortet natürlich meine Frage. 👍

        Geht es Dir darum, Zeilenumbrüche zu beseitigen, wenn zwei BBCodes aufeinander folgen und zwischen ihnen nur Whitespace ist?

        Nicht nur. Es könnte auch sein, dass der BBcode-Parser irgendetwas zu einem Blockelement wandelt. Dann müsste IMHO auch kein weiteres <br> mehr folgen.

        Also z.B. an Stellen wie hier:

        [tr][td][/td]
        [td]dzerh[/td]
        [/tr]
        

        Der BBCode-Konverter sollte daraus dies machen:

        <tr><td></td>
        <td>dzerh</td>
        </tr>
        

        Na eigentlich wärs schöner, wenn er daraus

        <tr><td></td><td>dzerh</td></tr>
        

        machen würde. 😉

        und das ist soweit okay. Aber dein nl2br, mit dem Du in normalem Text Zeilenumbrüche erhalten willst, schießt Dir quer und setzt da <br> ein. Das dürfte das Problem sein.

        Genau so sieht es aus.

        Im ersten Schritt könnte man versuchen, nur dann ein <br> einzusetzen, wenn man zwei Zeilenumbrüche hintereinander findet.

        Du weißt doch selber, dass User Zeilenumbrüche gerne als Formatierungselement einsetzen, nicht wahr? Das riecht nach Problemen... 😉

        Dein System würde sich dann ähnlich wie das Forum verhalten: wenn Du keine Leerzeile machst, wird der Text zu einem Paragraphen zusammengepappt.

        Was ich auch hier im Forum als "unglücklich" empfinde.

        Die CForum-Sonderlocke, dass ein Zeilenumbruch erhalten bleibt, wenn eine Zeile auf zwei Leerstellen endet, so wie hier,

        wußt' ich gar nicht. 😉

        Danke weiter für Deine Hilfe. Aber bisher gefallen mir die Lösungen noch nicht wirklich gut 😏

        Mal weiter drüber nachdenken...

        Jörg

        1. Hallo Jörg,

          Das riecht nach Problemen...

          Das stinkt alles gewaltig, ich weiß.

          Aber man muss ja irgendeine Form von Konsistenz drin haben.

          Wenn Du möchtest, dass Zeilenumbrüche generell einfach erhalten bleiben und nicht vom Browser zusammengeschnurrt werden, gibt's immer noch die CSS Eigenschaft white-space: pre. Dann bleiben Spaces alle schön wie sie sind, und deine <br> können keinen Schaden anrichten.

          white-space hat unterschiedliche Werte für unterschiedliche Anforderungen; vielleicht kannst Du Dein Problem ja auch damit erschlagen. Die im Wiki aufgeführten Werte für white-space haben alle eine breite Browserunterstützung, bis in den IE-Keller hinein. Eine neuer Wert - break-spaces - steht noch nicht drin; der ist von Mitte 2019.

          Und ein User, der unbedingt Fließtext haben will, findet dafür vielleicht noch einen passenden BBCode (den Du im Zweifelsfall selbst hinzufügst 😉, das Ding ist ja plugin-fähig).

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hallo Rolf,

            Wenn Du möchtest, dass Zeilenumbrüche generell einfach erhalten bleiben und nicht vom Browser zusammengeschnurrt werden, gibt's immer noch die CSS Eigenschaft white-space: pre. Dann bleiben Spaces alle schön wie sie sind, und deine <br> können keinen Schaden anrichten.

            Habe ich auch schon drüber nachgedacht. Die haben aber wieder andere Problemchen, die sie mitbringen. Das Gesamtproblem bei der Umsetzung von BBcode ist nicht ganz unbekannt, wie mir scheint 😉

            Ich überlege gerade, ob ich so (wie folgt) nicht ganz gut fahre (wohlwissend, dass ich die Tag-Liste noch ergänzen muss / img fehlt z.b. noch).

            // Text einlesen
            $text = $_REQUEST['myTextarea'];
            
            # Zeilenumbrüche korrigieren
            $text = str_replace("\r\n","\n",$text); # windows -> linux
            $text = str_replace("\r","\n",$text); # mac -> linux
            
            # array bilden
            $arr_text = explode("\n",$text);
            $neuerText = '';
            
            foreach($arr_text as $zeile) {
                if (!preg_match("/\[\/?(div|li|ul|td|tr|table|code|quote|list|ol|hr|th|tbody|row|h[123456]|youtube)\]$/",$zeile)) {
                    $zeile = $zeile."\n";
                }
             $neuerText .= $zeile;
            }
                echo $parser->parse($neuerText)
                    ->detect_links()
                    ->detect_emails()
                    ->detect_emoticons()
                    ->get_html();
            

            Das sieht bisher im Ergebnis ganz passabel aus.

            Jörg