Paul 14: Versand von Hochkomma in Web-Formular

Hallo, ich habe ein (php-) Formular, das sich selbst aufruft, um eine Vorschau vor dem endgültigen Absenden zu generieren. Das klappt auch wunderbar, nur wenn von Userseite ein Hochkomma eingegeben wurde, bricht der versendete Inhalt dann genau an dieser Stelle ab.

Die entsprechende Codezeile ohne Eingabe eines Hochkommas sieht so aus:

<input type='hidden' name='message_v' value='Guns n Roses' />

Man kann sich vorstellen, was passiert, wenn jemand hier ein vor dem n ein Hochkomma eingibt:

<input type='hidden' name='message_v' value='Guns 'n Roses' />

Und schon haben wir den Salat. Ich habe da einen Drehwurm im Hirn, aus dem ich alleine nicht rauskomme: Wenn ich die message vorher beispielsweise mit addslashes() escape, bekomme ich im Vorschau-Formular hübsch Backslashes, was aber nichts an der Problematik ändert, denn das HTML-Formular bricht dann einfach hinter dem Backslash ab, weil dort ja trotzdem ein Hochkomma steht, das den "value" beendet:

<input type='hidden' name='message_v' value='Guns \'n Roses' />

Wann, wie bzw. wo muss ich also für die korrekte Behandlung des Hochkommas sorgen?

akzeptierte Antworten

  1. Hallo Paul 14,

    Wann, wie bzw. wo muss ich also für die korrekte Behandlung des Hochkommas sorgen?

    Im HTML-Kontext kannst du es als &apos; kodieren. Oder du verwendest für die Ausgabe im HTML-Kontext htmlspecialchars(), was dir diese Aufgabe abnimmt. Das sollte man grundsätzlich tun, wenn unbekannte Zeichenfolgen ausgegeben werden sollen.

    Bis demnächst
    Matthias

    --
    Du kannst das Projekt SELFHTML unterstützen,
    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
    1. Hallo Paul 14,

      Wann, wie bzw. wo muss ich also für die korrekte Behandlung des Hochkommas sorgen?

      Im HTML-Kontext kannst du es als &apos; kodieren. Oder du verwendest für die Ausgabe im HTML-Kontext htmlspecialchars(), was dir diese Aufgabe abnimmt. Das sollte man grundsätzlich tun, wenn unbekannte Zeichenfolgen ausgegeben werden sollen.

      ... und den Schalter ENT_QUOTES nicht vergessen :-)

      LG + Gesundheit!
      Localhorst

      1. Danke, Localhorst, das war die Lösung! ich hatte htmlspecialchars() in meinem code, aber ohne ENT_QUOTES, weil ich dachte, dass standardmäßig ohne zusatzflag alle Zeichen umkodiert werden. War aber nicht so. Jetzt klappts:-)

      2. Tach!

        ... und den Schalter ENT_QUOTES nicht vergessen :-)

        Ja, damit kann man im vorliegenden Fall das Problem lösen. Aber will man dauernd das Flag angeben? Eigentlich nicht. Und deshalb sollte man die Werte von HTML-Attributen mit doppelten Anführungszeichen einfassen, auch wenn die Regeln einfache Anführungszeichen erlauben.

        dedlfix.

        1. Verstehe. Aber was tue ich, wenn ich z.B. HTML-Code wie diesen:

          <input type='hidden' name='message_v' value='$message_v' />
          

          an eine Variable übergeben möchte, muss ich ihn ja in Anführungszeichen setzen, also so:

          $meineVariable="<input type='hidden' name='message_v' value='$message_v' />"
          

          Womit ich beide Arten von Anführungszeichen benötige, oder nicht? Jetzt mal abgesehen von der Nowdoc-Syntax...

          1. Hallo Paul 14,

            <input type='hidden' name='message_v' value='$message_v' />
            

            an eine Variable übergeben möchte, muss ich ihn ja in Anführungszeichen setzen, also so:

            Das ist nicht klug,

            $meineVariable="<input type='hidden' name='message_v' value='$message_v' />"
            

            eine solche Variable haben zu wollen.

            <?php
            
            // php code, der alle nötigen Variablen liefert
            
            ?>
            
            <input type="hidden" name="message_v" value="<?=$message_v?>">
            

            Bis demnächst
            Matthias

            --
            Du kannst das Projekt SELFHTML unterstützen,
            indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
            1. Hallo,

              <input type='hidden' name='message_v' value='$message_v' />
              

              an eine Variable übergeben möchte, muss ich ihn ja in Anführungszeichen setzen, also so:

              Das ist nicht klug,

              $meineVariable="<input type='hidden' name='message_v' value='$message_v' />"
              

              eine solche Variable haben zu wollen.

              <?php
              
              // php code, der alle nötigen Variablen liefert
              
              ?>
              
              <input type="hidden" name="message_v" value="<?=$message_v?>">
              

              Wo werden die Werte escaped?
              Das kann man so leider nicht sehen.

              BTW:
              Man könnte den Weg auch anders herum gehen, und sich eine kleine wiederverwendbare Funktion erstellen, die dan HTML-String aus einem Array heraus erstellt. Dann kümmert sich die Funktion zukünftig um das Escapen und den Zusammenbau.

              LG + Gesundheit
              Localhorst

              LG

              1. Hallo localhorst,

                Wo werden die Werte escaped?
                Das kann man so leider nicht sehen.

                Ja, richtig.

                Bis demnächst
                Matthias

                --
                Du kannst das Projekt SELFHTML unterstützen,
                indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
          2. Tach!

            Verstehe. Aber was tue ich, wenn ich z.B. HTML-Code wie diesen:

            Ich fahre am besten, indem ich generell für PHP-Code die einfachen Anführungszeichen verwende. Nur in Situationen, in denen ich eine Variablenauflösung oder Escapesequenz ( wie \n) brauche, verwende ich als Ausnahme die doppelten.

            <input type='hidden' name='message_v' value='$message_v' />
            

            an eine Variable übergeben möchte, muss ich ihn ja in Anführungszeichen setzen, also so:

            $meineVariable="<input type='hidden' name='message_v' value='$message_v' />"
            

            Womit ich beide Arten von Anführungszeichen benötige, oder nicht?

            Diese Situation kommt nicht vor, weil dabei der Kontextwechsel nicht beachtet wird.

            $meineVariable = '<input type="hidden" name="message_v" value="' .
                htmlspecialchars($message_v) . '" />';
            

            Der Kontextwechsel sollte immer direkt an Ort und Stelle beachtet werden, damit man sieht, dass er nicht vergessen wurde.

            Somit löst sich die Variablenauflösung in Wohlgefallen auf und mit dieser die Notwendigkeit der doppelten Anführungszeichen. Aber: Der Code ist nun nicht mehr einfach lesbar, weil der String zusammengestückelt wird. Abhilfe kann sprintf() schaffen.

            $meineVariable = sprintf('<input type="hidden" name="message_v" value="%s" />',
                htmlspecialchars($message_v));
            

            Aber auch das ist noch nicht der Weisheit letzter Schluss. Welcher zwingender Grund besteht denn, den HTML-Code in einer Variable haben zu wollen? Wenn man sich an das EVA-Prinzip (Eingabe → Verarbeitung → Ausgabe) hält, ist die Ausgabe kein Teil der Verarbeitung. Sie kommt am Ende und kann direkt erfolgen.

            // Ende der Verarbeitung
            ?>
            ...
            <form ...>
            ...
            <input type="hidden" name="message_v" value="<?=htmlspecialchars($message_v))" />
            ...
            </form>
            ...
            

            Damit erübrigen sich die Anführungszeichen von selbst. Außerdem ist der HTML-Code nun nicht mehr in einem String versteckt. Eine Menge Editoren und IDEs können nun auch mit Syntaxhervorhebung und weiterer Unterstützung dienen, weil sie den HTML-Code als solchen erkennen können und nicht nur als beliebige Daten sehen.[1]

            Abschließend ist noch zu sagen, dass man auch im Ausgabeteil Logik benötigt. Zum Beispiel weil man Array-Daten in einer Schleife ausgeben möchte, oder weil eine Bedingung unterschiedliche Ausgaben benötigt. Wenn die Bedingung komplex zu ermitteln ist, steht sie im V-Teil und wird als einfacher Wert (z.B. true/false) an den Ausgabeteil gegeben. Im Ausgabeteil selbst steht hauptsächlich HTML-Code. Für die benötigten Kontrollstrukturen der Logik (if/for) wird stellenweise wieder in den PHP-Modus gewechselt. Man kann dabei die alternative Syntax für Kontrollstrukturen verwenden, aber das ist Geschmackssache.

            Beispiel:

            <?php if ($errors) { ?>
            
            <p>Verarbeitung fehlgeschlagen. Gründe:</p>
            
            <ul>
            <?php foreach ($errors as $error) { ?>
              <li><?= htmlspecialchars($error) ?></li>
            <?php } ?>
            </ul>
            
            <?php } else { ?>
            
            Ausgabe der Daten, wenn kein Fehler auftrat
            
            <?php } ?>
            

            oder in alternativer Syntax:

            <?php if ($errors): ?>
            
            <p>Verarbeitung fehlgeschlagen. Gründe:</p>
            
            <ul>
            <?php foreach ($errors as $error): ?>
              <li><?= htmlspecialchars($error) ?></li>
            <?php endforeach; ?>
            </ul>
            
            <?php else: ?>
            
            Ausgabe der Daten, wenn kein Fehler auftrat
            
            <?php endif; ?>
            

            dedlfix.


            1. Ja, es gibt auch einige gute IDEs, die auch eingebetteten Code in String erkennen und unterstützen können. ↩︎

            1. Moin Dedlfix,

              Aber auch das ist noch nicht der Weisheit letzter Schluss. Welcher zwingender Grund besteht denn, den HTML-Code in einer Variable haben zu wollen?

              Wie gestaltest Du die datengesteuerte, bzw. revolvierende Ausgabe, z.B. von Tabellen und/oder Listen?

              Da ist der HTML-Anteil zwangsläufig variabel.

              LG + Gesundheit
              Localhorst

              1. Hallo localhorst,

                Wie gestaltest Du die datengesteuerte, bzw. revolvierende Ausgabe, z.B. von Tabellen und/oder Listen?

                Da ist der HTML-Anteil zwangsläufig variabel.

                Na so wie im $errors-Beispiel gezeigt, oder verstehe ich dich falsch?

                Bis demnächst
                Matthias

                --
                Du kannst das Projekt SELFHTML unterstützen,
                indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                1. Hallo Matthias,

                  Wie gestaltest Du die datengesteuerte, bzw. revolvierende Ausgabe, z.B. von Tabellen und/oder Listen?

                  Da ist der HTML-Anteil zwangsläufig variabel.

                  Na so wie im $errors-Beispiel gezeigt, oder verstehe ich dich falsch?

                  Wo finde ich das Beispiel? Habe da wohl selber gerade einen 404-Status...

                  Ich persönlich packe bei linearen Tabellen (alle Zeilen gleich aufgebaut) immer einen Tabellenblock (<tr> bis </tr>) in eine Variable. Die kann auch aus einer Vorlagendatei geladen werden. Damit wären die Quotierungsprobleme schon mal beseitigt. Für die Werte werden Platzhalter vorgehalten, die per str_replace dann fertig escaped eingestanzt werden.

                  Das ist aber für kleine Angelegenheiten oversized. Darum interessieren mich immer andere Lösungswege.

                  Und meine Versuche auch noch einen AL für die zu verwendende Auszeichnungssprache vorzusehen, waren bisher nicht erfolgreich. Anregungen sind daher höchst willkommen.

                  LG + Gesundheit!
                  Localhorst

                  1. Hallo localhorst,

                    Wo finde ich das Beispiel? Habe da wohl selber gerade einen 404-Status...

                    https://forum.selfhtml.org/self/2021/jan/21/versand-von-hochkomma-in-web-formular/1780758#m1780758, ganz unten im Beitrag

                    Bis demnächst
                    Matthias

                    --
                    Du kannst das Projekt SELFHTML unterstützen,
                    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                  2. Moin,

                    Na so wie im $errors-Beispiel gezeigt, oder verstehe ich dich falsch?

                    Wo finde ich das Beispiel? Habe da wohl selber gerade einen 404-Status...

                    guckstu hier.

                    Live long and pros healthy,
                     Martin

                    --
                    Für welches Tier mühen wir uns am meisten ab? - Für die Katz'.
                  3. Hallo localhorst,

                    Ich persönlich packe bei linearen Tabellen (alle Zeilen gleich aufgebaut) immer einen Tabellenblock (<tr> bis </tr>) in eine Variable.

                    Warum packst Du nicht die Daten für die Zellen in eine Variable? HTML Aufbereitung gehört nicht in die Verarbeitung. Und ein solches Datenarray existiert doch vermutlich ohnehin. Das gibst Du dann im A-Teil der EVA einfach aus. Im wirklichen Leben wird das komplexer sein, sicher. Und eventuell gibt es Logik, die die Zellenaufbereitung oder die Datenauswahl steuert. Je nach Komplexität macht man das im Ausgabeteil, oder schafft sich vorher eine passende Sicht auf die Daten. Entweder über ein weiteres Array, oder über eine Generatorfunktion - ab PHP 5.5 verfügbar.

                    <table>
                    <?php
                    foreach ($daten as $zeile):
                    ?>
                          <tr>
                             <td><?= htmlspecialchars($zeile['id'])?></td>
                             <td><?= htmlspecialchars($zeile['name'])?></td>
                             <td><?= htmlspecialchars($zeile['vorname'])?></td>
                             <td><?= $zeile['geburtsdatum']->format('d.m.Y')?></td>
                          </tr>
                    <?php
                    endforeach;
                    ?>
                    </table>
                    

                    Rolf

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

                      abgesehen davon, dass ein paar php-Tags an der falschen Stelle sitzen 😂: So mach ich das auch.

                      Bis demnächst
                      Matthias

                      --
                      Du kannst das Projekt SELFHTML unterstützen,
                      indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                      1. Hallo Matthias,

                        Autsch. Fixed.

                        Rolf

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

                      Ich persönlich packe bei linearen Tabellen (alle Zeilen gleich aufgebaut) immer einen Tabellenblock (<tr> bis </tr>) in eine Variable.

                      Warum packst Du nicht die Daten für die Zellen in eine Variable?

                      Das widerspricht der Trennung von passiver und aktiver Logik und somit dem Trennungsgrundsatz der Workflowschichten.

                      Die Gestalterin der Beschreibungsvorgaben (HTML), diejenige der Gestaltung (CSS) und diejenige der Programmlogik (hier PHP) sollen strikt trennbar sein/bleiben. Datenhaltung (z.B. mySQL) wäre dann eine zusätzliche Schicht. Und diese wird dann nochmals getrennt in DEL, DML, DDL und RLL (Request Logging), sowie Rechtemanagement.

                      Das (erster Teil) ist mit deiner Methode (schon) unmöglich.

                      LG + Gesundheit
                      Localhorst

                      1. Tach!

                        Das widerspricht der Trennung von passiver und aktiver Logik und somit dem Trennungsgrundsatz der Workflowschichten.

                        Das kann man so machen, ist aber meist nicht praktisch, und wird auch in vielen Frameworks nicht auf diese Weise getrennt.

                        Die Gestalterin der Beschreibungsvorgaben (HTML), diejenige der Gestaltung (CSS) und diejenige der Programmlogik sollen strikt trennbar sein/bleiben.

                        Wie kennzeichnet man aber nun, an welcher Stelle im Template Logik (Bedingung, Wiederholung, Variablen) stattzufinden hat? Wenn man dazu eine Sprache erfindet, kommt man vom angeblichen Regen in die Traufe. Statt PHP im HTML-Teil hat man nun Elemente einer weiteren Template-Syntax darin, und ein zusätzliches System, das diese Syntax parsen und verarbeiten muss. Am Ende bildet man damit auch nur nach, was PHP bereits mitbringt. Der Nutzen, den man davon hat, muss sich ganz schön anstrengen, um Aufwand und Komplexität zu rechtfertigen.

                        dedlfix.

                      2. Hallo localhorst,

                        Warum packst Du nicht die Daten für die Zellen in eine Variable?

                        Das widerspricht der Trennung von passiver und aktiver Logik und somit dem Trennungsgrundsatz der Workflowschichten.

                        Ich würde das gern diskutieren, aber mir fehlen die Worte. Vielleicht habe ich anderswo gelernt als du...

                        Aktive und passive Logik? Sagt mir nichts. Tante Google sagt das auch nichts. Könntest Du das bitte definieren, bzw. zum Nachlesen eine Quelle für eine Definition nennen?

                        Mir bekannte Schichten sind Datenhaltung, Businesslogik, Steuerungslogik und Präsentation. Die werden unter dem Namen Softwareschichten geführt. Meinst Du die? Oder sind Workflowschichten etwas anderes, mir ebenfalls unbekanntes?

                        Rolf

                        --
                        sumpsi - posui - obstruxi
                    3. Tach!

                      [...] oder schafft sich vorher eine passende Sicht auf die Daten.

                      Für die Leser, die mit dem Begriff "Sicht" (oder englisch View oder auch Data View, Table View) nichts anzufangen wissen, ein Beispiel:

                      Ein Personendatensatz in einer DB enthält unter anderem ein Geburtsdatum. In der Ausgabe soll das Alter zu sehen sein. Wenn das Projekt insgesamt hinreichend trivial ist, könnte man einfach eine Hilfsfunktion schreiben, die das bei der Ausgabe umrechnet. Damit vermischt man aber Datenverarbeitung und Ausgabe. Eine Sicht erstellt man, indem man vom Datensatz aus dem DBMS eine Kopie erzeugt, dabei aber die nicht benötigten Teile weglässt (sofern man sie nicht gleich beim SELECT ausklammern kann), und im Falle des Beispiels ein Feld hinzufügt, in dem das berechnete Alter abgelegt wird. Der Vorgang dazu heißt Mapping, und es gibt dafür auch Array-Funktionen für Massendaten (array_map() in PHP oder in Javascript die Methode map() vom Array-Objekt). Somit erhält man sozusagen einen temporären Datensatz, speziell für die Ausgabe aufbereitet, also eine andere Sicht auf die Daten.

                      dedlfix.

                      1. Hallo dedlfix,

                        Eine Sicht erstellt man, indem man vom Datensatz aus dem DBMS eine Kopie erzeugt, dabei aber die nicht benötigten Teile weglässt (sofern man sie nicht gleich beim SELECT ausklammern kann), und im Falle des Beispiels ein Feld hinzufügt, in dem das berechnete Alter abgelegt wird.

                        Auch innerhalb des SELECT – oder?

                        Bis demnächst
                        Matthias

                        --
                        Du kannst das Projekt SELFHTML unterstützen,
                        indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                        1. Tach!

                          Eine Sicht erstellt man, indem man vom Datensatz aus dem DBMS eine Kopie erzeugt, dabei aber die nicht benötigten Teile weglässt (sofern man sie nicht gleich beim SELECT ausklammern kann), und im Falle des Beispiels ein Feld hinzufügt, in dem das berechnete Alter abgelegt wird.

                          Auch innerhalb des SELECT – oder?

                          Ja, im Prinzip kann man das Alter auch gleich im SELECT berechnen lassen. Dann erstellt man bereits beim Abfragen eine Sicht/View auf die eigentlich gespeicherten Daten. Andererseits gibt es auch genügend Argumente, das nicht so zu tun. Das Alter ist ja nur ein einfaches Beispiel. Je komplexer die Aufgabenstellung wird, desto umfangreicher wird auch der SELECT-Teil. Eine Möglichkeit ist, die Komplexität in einer aus SQL bekannten VIEW zu verstecken. Damit verlagert man aber Aufgaben ins DBMS und man muss "hier" und "da" berücksichtigen, wenn Änderungen oder Fehlersuche anstehen.

                          Ein Punkt sind auch ORMs. Die machen die 08/15-Datenabfragen meist recht einfach, aber Formeln im SELECT anzuwenden ist oft nur umständlich möglich.

                          Daten kommen heutzutage aber auch aus anderen Quellen, wo man keinen Zugriff oder nur begrenzte Möglichkeiten hat, die SELECT-Klausel oder das dortige Äquivalent dazu zu formulieren. Dann muss man in Nachhinein bei sich im Programmcode die gewünschte Sicht schaffen.

                          Wie auch immer, ob man diesen oder jenen Weg geht, sollte man sowieso besser anhand der individuellen Projektanforderungen entscheiden.

                          dedlfix.

                      2. Hallo dedlfix,

                        ich meinte keine DB Sicht. Da gibt's die auch, sicher, aber man kann auch innerhalb der Anwendung ein Array mit den benötigten Daten für das UI erzeugen (oder auch was komplexeres, ein ViewModel), und zumindest in meinem Umfeld haben wir das auch eine Sicht genannt.

                        Wenn man es mit einer Generatorfunktion macht, wird der Sichtaspekt noch deutlicher - denn ein DBMS tut ja mit einem VIEW nichts anderes, als ein SELECT-Ergebnis als Table bereitzustellen, damit ich on the fly darauf eine weitere Query anwenden kann.

                        Rolf

                        --
                        sumpsi - posui - obstruxi
                        1. Tach!

                          ich meinte keine DB Sicht. Da gibt's die auch, sicher, aber man kann auch innerhalb der Anwendung ein Array mit den benötigten Daten für das UI erzeugen (oder auch was komplexeres, ein ViewModel), und zumindest in meinem Umfeld haben wir das auch eine Sicht genannt.

                          Ja, letzteres und nicht den konkreten Fall SQL-VIEW wollte ich etwas erklären. Mir schien, dass diese Erklärung für Paul 14 nützlich wäre.

                          dedlfix.

              2. Tach!

                Wie gestaltest Du die datengesteuerte, bzw. revolvierende Ausgabe, z.B. von Tabellen und/oder Listen?

                Der V-Teil erstellt/berechnet die für die Ausgabe benötigten Daten. Der Ausgabeteil hat alles direkt verwendbar zur Verfügung, um die Ausgabe zu erstellen. Logik dafür und Hilfsfunktionen kann er verwenden.

                Wenn das Projekt sehr komplex ist, nehme ich ein Framework, das dafür Lösungsvorschläge anbietet, zum Beispiel, indem es in sich selbständige Module anbietet, die man bei Bedarf mehrfach in die Ausgabe einbinden kann.[1] Im einfachsten Fall kann man Funktionen zur Kapselung verwenden, die im Ausgabeteil aufgerufen sich um komplexere Teile der Ausgabe kümmern.

                dedlfix.


                1. Frag nicht nach Namen, ich hab schon lange keinen Bedarf gehabt, so etwas mit PHP umzusetzen. Aber wenn ich was nennen soll, dann Angular mit seinen Components oder ASP.NET MVC mit Views und darin Partials zum Kapseln. ↩︎

  2. Hallo Paul,

    neben den Antworten von @Matthias Apsel und @localhorst sei der Vollständigkeit halber noch auf den berühmten Wiki-Artikel zum Kontextwechsel verwiesen.

    Viele Grüße
    Robert

    1. Danke für den Tipp!