Jörg: DB -> JS -> Textarea

Hallo Forum,

ich ermittle einen Wert aus der DB und stelle ihn in HTML dar. Zugleich ist er über einen Button anklickbar, sodaß er dann in eine Textarea per onclick-event eingesetzt werden soll.

Das klappt nicht, wenn der Wert in der DB mehrzeilig ist, weshalb ich dachte, dass ich den Wert mit javascript_escape() behandeln könnte.

Naja, es hilft insofern, als dass meine Javascriptfunktion keinen Fehler mehr wirft. Aber dafür steht dann in der Textarea anstelle des Zeilenumbruchs auch ein \n.

Also Zeile1\nZeile2.

Versucht habe ich schon, diesen Wert dann mit string.replace() zu behandeln, aber ich bekomme das \n nicht gegen einen echten Textarea-Zeilenumbruch ausgetauscht, egal, was ich zu wechseln versucht habe.

Wie geht man sowas an?

Jörg

  1. Wie geht man sowas an?

    Mit Ruhe und Bedacht:

    <html>
    <textarea id="ta1"></textarea>
    <br>
    <button onclick='document.getElementById("ta1").innerHTML="Geht\ndas?"'>Klick</button>
    </html>
    

    Sodann:

    <?php
    $s='Geht
    das?';
    $s=str_replace( "\n", '\n', $s );
    ?>
    <html>
    <textarea id="ta1"></textarea>
    <br>
    <button onclick='document.getElementById("ta1").innerHTML="<?=$s;?>"'>Klick</button>
    </html>
    
  2. Hallo Jörg,

    ich ermittle einen Wert aus der DB und stelle ihn in HTML dar.

    Wie stellst Du hier sicher, dass mehrzeiliger Text auch mehrzeilig dargestellt wird?

    weshalb ich dachte, dass ich den Wert mit javascript_escape() behandeln könnte.

    Was ist das für eine Funktion? Die kenne ich weder in PHP noch in JavaScript.

    Mein eigenes kleines Experiment hat ein p Element mit mehrzeiligem Text in eine textarea übertragen (textContent nach value kopiert) und es gab gar kein Problem. https://jsfiddle.net/Rolf_b/qxybwv2k/

    Rolf

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

      weshalb ich dachte, dass ich den Wert mit javascript_escape() behandeln könnte.

      Was ist das für eine Funktion? Die kenne ich weder in PHP noch in JavaScript.

      Oh, hatte ganz vergessen, dass das keine php-Funktion ist.

      
      /**
       * Maskiert Sonderzeichen für den JavaScript-Kontext.
       *
       * @param $value string Der zu behandelnde Wert.
       * @return string
       */
      function javascript_escape($value)
      {
          return strtr((string)$value,array(
              "'" => '\\\'',
              '"' => '\"',
              '\\' => '\\\\',
              "\n" => '\n',
              "\r" => '\r',
              "\t" => '\t',
              chr(12) => '\f',
              chr(11) => '\v',
              chr(8) => '\b',
          ));
      }
      

      Und ich stelle das in HTML einfach 1:1 wie aus der DB dar, also ohne nl2br().

      Das onclick-event selber hat verschiedene Parameter, unter anderem eben javascript_escape($bemerkungen). Und da Bemerkungen in der DB mehrzeilig sein können, muss ich mich um die Zeilenumbrüche irgendwie "kümmern), denn wennn ich sie "raw" übergebe, erhalte ich einen JS-Fehler.

      Edit:

      Habe festgestellt, dass der Parameter anstelle der Zeilenumbrüche jeweils Doppelshlash n enthält.

      Wenn ich die Funktio9n nun wie folgt umbaue, dann werden die Zeilenumbrüche in der Textarea korrekt dargestellt.

      function javascript_escape($value)
      {
          return strtr((string)$value,array(
              "'" => '\\\'',
              '"' => '\"',
              '\\' => '\\\\',
              "\n" => '\n',
              "\\n" => '\n',
              "\r" => '\r',
              "\t" => '\t',
              chr(12) => '\f',
              chr(11) => '\v',
              chr(8) => '\b',
          ));
      }
      

      Jörg

      1. Hallo,

        Und ich stelle das in HTML einfach 1:1 wie aus der DB dar, also ohne nl2br().

        ich gewinne so langsam den Eindruck, dass du das eben nicht ins HTML schreibst, sondern mit PHP ein Stück Javascript erzeugst.

        Das onclick-event selber hat verschiedene Parameter, unter anderem eben javascript_escape($bemerkungen). Und da Bemerkungen in der DB mehrzeilig sein können, muss ich mich um die Zeilenumbrüche irgendwie "kümmern), denn wennn ich sie "raw" übergebe, erhalte ich einen JS-Fehler.

        Dann verrate uns bloß nicht, was für einen. Wahrscheinlich einen gewöhnlichen Syntaxfehler.

        Edit:

        Habe festgestellt, dass der Parameter anstelle der Zeilenumbrüche jeweils Doppelshlash n enthält.

        Und wo kommt das wieder her?

        Wenn ich die Funktio9n nun wie folgt umbaue, dann werden die Zeilenumbrüche in der Textarea korrekt dargestellt.

        Kommentare von mir ergänzt.

        function javascript_escape($value)
        {
            return strtr((string)$value,array(
                "'" => '\\\'',   // ersetzt Single Quote durch Backslash, Single Quote
                '"' => '\"',     // ersetzt Double Quote durch Backslash, Double Quote
                '\\' => '\\\\',  // ersetzt Backslash durch zweimal Backslash
                "\n" => '\n',    // ersetzt Linefeed durch Backslash, n
                "\\n" => '\n',   // ersetzt Backslash, n durch Backslash, n - WTF??
                "\r" => '\r',    // ersetzt Zeilenumbruch durch Backslash, r
                "\t" => '\t',    // ersetzt Tabulator durch Backslash, t
                chr(12) => '\f', // ersetzt Seitenvorschub durch Backslash, f - wer benutzt sowas?
                chr(11) => '\v', // ersetzt Vertical Tab durch Backslash, v - wer benutzt denn das?
                chr(8) => '\b',  // ersetzt Backspace durch Backslash, b
            ));
        }
        

        Die Funktion ersetzt also einige der üblichen Verdächtigen, die in Strings Ärger machen können, durch die gängigen C- oder PHP-Escape-Sequenzen. Nur die Ersetzung, die du noch hinzugefügt hast, ergibt absolut keinen Sinn - es sei denn, da ist irgendwo noch mehr Magic Code, den wir nicht kennen, und der vorher schon irgendwas kaputtmacht.

        Frage an die Datenbank-Fachleute: Liefert die mysqli-Schnittstelle echte Linefeeds oder auch schon die Escape-Sequenz Backslash, n?

        Einen schönen Tag noch
         Martin

        --
        "Was sind denn das für Beeren?" - "Das sind Blaubeeren." - "Warum sind sie dann rot?" - "Weil sie noch grün sind."
        1. Hallo Der,

          Liefert die mysqli-Schnittstelle echte Linefeeds oder auch schon die Escape-Sequenz Backslash, \n?

          (Im Forum muss man Backslashes ebenfalls escapen)

          Ja.

          Kommt halt drauf an was in der DB steht. Und mysqli macht die Sache nicht einfacher, wenn man SQL Literale verwendet.

          Ich habe mal diese Codesequenz laufen lassen:

          $db->query("INSERT INTO foo (col) VALUES('1 Hello\nWorld')");
          $db->query("INSERT INTO foo (col) VALUES('2 Hello\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('3 Hello\\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('4 Hello\\\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('5 Hello\\\\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('6 Hello\\\\\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('7 Hello\\\\\\\nWorld'))";
          $db->query("INSERT INTO foo (col) VALUES('8 Hello\\\\\\\\nWorld'))";
          

          Wenn man überlegt, was PHP daraus macht, werden dadurch die folgenden SQL Statements an die DB geschickt. Linefeeds im Statement sind Linefeeds, die die DB bekommt!

          INSERT INTO foo (col) VALUES('1 Hello
          World')
          
          INSERT INTO foo (col) VALUES('2 Hello\nWorld')
          
          INSERT INTO foo (col) VALUES('3 Hello\
          World')
          
          INSERT INTO foo (col) VALUES('4 Hello\\nWorld')
          
          INSERT INTO foo (col) VALUES('5 Hello\\
          World')
          
          INSERT INTO foo (col) VALUES('6 Hello\\\nWorld')
          
          INSERT INTO foo (col) VALUES('7 Hello\\\
          World')
          
          INSERT INTO foo (col) VALUES('8 Hello\\\\nWorld')
          

          Das Ergebnis in HeidiSQL war (<lf> steht für echten Linefeed):

          1 Hello<lf>World
          2 Hello<lf>World
          3 Hello<lf>World
          4 Hello\nWorld
          5 Hello\<lf>World
          6 Hello\<lf>World
          7 Hello\<lf>World
          8 Hello\\nWorld
          

          Offensichtlich ist für MariaDB (und vermutlich auch MYSQL) die Sequenz \n und \<lf> gleichwertig und wird zu einem Linefeed umgewandelt. Und ein unescaptes <lf> wird ebenfalls akzeptiert.

          Mit einem Prepared Statement geht es besser:

          $stmt = $c->prepare("INSERT INTO foo (col) VALUES(?)");
          $stmt->bind_param("s", $name);
          
          $name = "1 Hello\nWorld"; $stmt->execute();
          $name = "2 Hello\\nWorld"; $stmt->execute();
          $name = "3 Hello\\\nWorld"; $stmt->execute();
          $name = "4 Hello\\\\nWorld"; $stmt->execute();
          

          Hier ist das Ergebnis weniger kopfzerbrechend, weil nur noch PHP das \ Zeichen auflöst:

          1 Hello<lf>World
          2 Hello\nWorld
          3 Hello\<lf>World
          4 Hello\\nWorld
          

          Nun die Abfrage.

          $result = $db->query("SELECT col FROM foo");
          while ($row = $result->fetch_assoc()) {
             echo "'$row[col]'\n";
          }
          

          ergibt

          '1 Hello
          World'
          '2 Hello\nWorld'
          '3 Hello\
          World'
          '4 Hello\\nWorld'
          

          FAZIT

          Ich kann Linefeeds in die SQL Datenbank schreiben und bekomme sie auch von PHP als Linefeeds zurück. Ohne Prepared Statements ist das aber eine Angelegenheit, bei der man jederzeit einige Zehen verlieren kann (weil man sich in den Fuß geschossen hat).

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hi,

            Liefert die mysqli-Schnittstelle echte Linefeeds oder auch schon die Escape-Sequenz Backslash, n?

            (Im Forum muss man Backslashes ebenfalls escapen)

            ich weiß, aber ich schrieb ja in Worten: Backslash, n.

            Ja.

            Okay, also kriegt Jörg eventuell doch schon "kaputte" Strings aus seiner DB.

            Einen schönen Tag noch
             Martin

            --
            "Was sind denn das für Beeren?" - "Das sind Blaubeeren." - "Warum sind sie dann rot?" - "Weil sie noch grün sind."
  3. Hallo Jörg,

    ich ermittle einen Wert aus der DB und stelle ihn in HTML dar.

    mit PHP? Ich hab das mal als weiteres Tag dazugenommen.

    Funktioniert das mit der Mehrzeiligkeit denn hier? - Für den HTML-Kontext müsstest du die Zeilenumbrüche ja durch <br> ersetzen. Das tut z.B. die PHP-Funktion nl2br().
    Oder nein, sie ersetzt nicht \n durch <br>, sondern \n durch <br>\n.

    Apropos: Wie werden denn die Zeilenumbrüche im Originalstring in der DB codiert? Üblich ist entweder ein Linefeed (\x0A) oder die Kombination CR+LF (\x0D\x0a). Ohne weitere Behandlung würden diese Zeichen in HTML aber nur als Leerzeichen angezeigt.

    Zugleich ist er über einen Button anklickbar, sodaß er dann in eine Textarea per onclick-event eingesetzt werden soll.

    Das heißt, das Javascript liest den Text wieder aus dem DOM-Textknoten? Damit stellt sich zum zweiten Mal die Frage: Wie bereitest du den String für die HTML-Anzeige auf, und was tust du, um die <br> beim Übertragen in die Textarea wieder zu echten Zeilenumbrüchen zu machen? Denn dort werden sie wieder so codiert, wie ich eingangs beschrieben habe. Ob nur LF oder CR+LF, weiß ich nicht sicher - ich meine letzteres.

    Das klappt nicht, wenn der Wert in der DB mehrzeilig ist, weshalb ich dachte, dass ich den Wert mit javascript_escape() behandeln könnte.

    Wie Rolf schon fragte: Wo ist diese Funktion her? Was genau tut sie?

    Naja, es hilft insofern, als dass meine Javascriptfunktion keinen Fehler mehr wirft. Aber dafür steht dann in der Textarea anstelle des Zeilenumbruchs auch ein \n.

    Dann scheint diese Funktion Zeilenumbrüche durch die Sequenz Backslash, Kleinbuchstabe n zu ersetzen. Das nützt dir aber für die Textarea nichts.

    Wie geht man sowas an?

    Indem du erstmal zeigst, was du wirklich hast und tust. Ich habe dich schon mehrmals kritisiert, weil deine Problembeschreibungen meistens nicht genug Information enthalten. Insbesondere keine klar nachvollziehbaren Codeauszüge und/oder Debug-Ausgaben.

    Einen schönen Tag noch
     Martin

    --
    "Was sind denn das für Beeren?" - "Das sind Blaubeeren." - "Warum sind sie dann rot?" - "Weil sie noch grün sind."
    1. Hallo Martin,

      Für den HTML-Kontext müsstest du die Zeilenumbrüche ja durch <br> ersetzen.

      Man kann, aber das macht das Leben nicht einfacher.

      Mein Fiddle verwendet white-space: pre-line. Schwupdiwup - alles läuft.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. n'Abend Rolf,

        Für den HTML-Kontext müsstest du die Zeilenumbrüche ja durch <br> ersetzen.

        Man kann, aber das macht das Leben nicht einfacher.

        Mein Fiddle verwendet white-space: pre-line. Schwupdiwup - alles läuft.

        oh, das ist schlau! 😀

        Tatsächlich habe ich nicht daran gedacht, dass man das rein per CSS lösen kann.

        Einen schönen Tag noch
         Martin

        --
        "Was sind denn das für Beeren?" - "Das sind Blaubeeren." - "Warum sind sie dann rot?" - "Weil sie noch grün sind."
        1. Hallo Martin,

          für einen Schmied ist alles ein Hammer…

          Rolf

          --
          sumpsi - posui - obstruxi