Ferdinand: PHP syntax error unexpected double-quoted string

Okaaay, ich bin raus

Ein eigentlich super simples PHP Skript

$conn = mysqli_connect("","root","","dbname");


$sql = "SELECT user, score, timestamp FROM myTable";
$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {
  while($row = mysqli_fetch_assoc($result)) {
    echo "user: " . $row["user"] . " - score: " . $row["score"] . " " . $row["timestamp"] . "<br>";
  }
} else {
  echo "0 results";
}

mysqli_close($conn);

erzeugt Parse error: syntax error, unexpected double-quoted string "user: " bezogen auf die die Zeile  echo "user: " . $row["user"] . " - score: " . $row["score"] . " " . $row["timestamp"] . "<br>";

ARG Hilfe?

Danke, Ferdinand

  1. Nein. Hab das getestet. Der Fehler liegt definitiv außerhalb des gezeigten Codes.

    → Oft ein vergessenes Quota weiter oben. Ein Editor mit Syntaxbeleuchtung hilft Dir.

    1. Hallo Raketenwilli,

      hast Du kein Bett? Oder trainierst Du für die Silvesternacht?

      Ich sehe aber auch im Umfeld der Zeile nichts inkriminierendes. Ein verschlabbertes " oder ; VOR dieser Stelle hätte sich bei diesem Code schon früher bemerkbar machen sollen. Find ich…

      Ferdinand,

      ist dieser Code alles, was in der PHP Datei drinsteht (außer einem einleitenden <?php natürlich)?

      Meine Hypothese 1: du guckst auf die falsche Stelle. Gibt's diese Zeile anderswo nochmal? Copy&Paste? Ein Parse Error wird normalerweise von Dateiname und Zeilennummer begleitet.

      Eine SEHR UNWAHRSCHEINLICHE Hypothese ist ein Zeichen im Code, das wie ein normales ASCII Zeichen aussieht, aber keins ist. Sowas kann passieren, wenn man Code aus dem wilden weiten Web kopiert und der Autor "schicke Typographie" im Code haben wollte oder geschützte Leerzeichen eingesetzt hat. Aber das hätte sich beim Kopieren hier ins Forum übertragen müssen, deshalb: Sehr unwahrscheinlich.

      Wenn die Stelle stimmt (bei vorhandenen Kopien lässt sich das zur Not durch Abändern von "user: " in "userin: " oder ähnlichem beweisen), aber viel Code vorneweg steht, kannst Du den gezeigten Code in eine andere PHP Datei kopieren und separat ausführen. Kommt dann kein Parse Error, fügst Du immer mehr (sinnvolle) Codestücke aus dem Original dazu, ausgehend von der kaputten Stelle aus nach oben. Da ein Parse Error vor der Ausführung entdeckt wird, könnte es nützlich sein, direkt hinter dem ersten <?php ein exit; zu notieren, damit PHP bei fehlendem Parse Error nichts ausführt, was ggf. Schaden anrichtet.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. hast Du kein Bett?

        Doch. Aber ich bin aus dem selben gefallen, weil jemand Auto gefahren ist, der das nicht hätte tun sollen. War ziemlich laut. Mit dem Auto macht der das nicht mehr.

    2. Ein Editor mit Syntaxbeleuchtung hilft Dir.

      Steht man völlig auf dem Schlauch, hilft auch das Entfernen der bemängelten Zeilen und, nach und nach, ihrer Umgebung, so oft bzw. weit, bis auch die Fehlermeldungen verschwinden.

      Das ist dann eine Art indirekter Eingrenzung mit dem Holzhammer – so lange kaputtkloppen, bis es heil ist.

  2. Hi,

    Ein eigentlich super simples PHP Skript erzeugt Parse error: syntax error, unexpected double-quoted string "user: " bezogen auf die die Zeile  echo "user: " . $row["user"] . " - score: " . $row["score"] . " " . $row["timestamp"] . "<br>";

    Hast Du das Skript hier reinkopiert, oder (inkl. automatischer Korrektur) hier eingetippt?

    Der Code sieht ok aus, auch der Highlighter macht keine verrückten Sachen damit, daher mein Verdacht, daß der hier gezeigte Code nicht identisch mit dem ist, der das Problem verursacht.

    cu,
    Andreas a/k/a MudGuard

  3. Moin Ferdinand,

    das

    echo "user: " . $row["user"] . " - score: " . $row["score"] . " " . $row["timestamp"] . "<br>";
    

    geht auch einfacher und mutmaßlich performanter, da die Einzelteile direkt in die Ausgabe geschrieben und nicht erst konkateniert werden müssen:

    echo "user: ", $row["user"], " - score: ", $row["score"],
        " ", $row["timestamp"], "<br>";
    

    Soweit ich das sehe, passen da die Quotes. Was fehlt: die kontextgerechte Behandlung der SQL-Rückgabe für HTML – verwende htmlspecialchars.

    Viele Grüße
    Robert

    1. @@Robert B.

      geht auch einfacher und mutmaßlich performanter, da die Einzelteile direkt in die Ausgabe geschrieben und nicht erst konkateniert werden müssen:

      echo "user: ", $row["user"], " - score: ", $row["score"],
          " ", $row["timestamp"], "<br>";
      

      Anstatt Konkatenation bietet sich ein Templatestring an:

      printf(
        "user: %1\$s – score: %2\$s %3\$s<br>",
        $row["user"], $row["score"], $row["timestamp"]
      );
      

      Oder doch nicht, siehe unten.

      Was fehlt: die kontextgerechte Behandlung der SQL-Rückgabe für HTML – verwende htmlspecialchars.

      Yep:

      printf(
        "user: %1\$s – score: %2\$s %3\$s<br>",
        htmlspecialchars($row["user"]),
        htmlspecialchars($row["score"]),
        htmlspecialchars($row["timestamp"])
      );
      

      Was noch fehlt: sinnvolles Markup anstatt <br>. Das sollte wohl eine Liste <ul>/<ol> mit Listitems <li> sein.

      Und überhaupt würde eine vernünftige Syntax das ganze Rumgemache mit Konkatenation erübrigen.

      🖖 Живіть довго і процвітайте

      --
      „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
      — @Grantscheam auf Twitter
      1. @@Gunnar Bittersmann

        Was noch fehlt: sinnvolles Markup anstatt <br>. Das sollte wohl eine Liste <ul>/<ol> mit Listitems <li> sein.

        Und überhaupt würde eine vernünftige Syntax das ganze Rumgemache mit Konkatenation erübrigen.

        Sieht dann so aus – Markup und feststehender Text wird direkt notiert, per echo werden nur die veränderlichen Daten ausgegeben. (Hier in der Kurzschreibweise <?=)

        <?php
          $conn = mysqli_connect("", "root", "" ,"dbname");
        
          $sql = "SELECT user, score, timestamp FROM myTable";
          $result = mysqli_query($conn, $sql);
        ?>
        
        <?php if (mysqli_num_rows($result) > 0): ?>
          <ul>
          <?php while($row = mysqli_fetch_assoc($result)): ?>
            <li>
              user: <?= htmlspecialchars($row["user"]) ?>
              - score: <?= htmlspecialchars($row["score"]) ?>
              <?= htmlspecialchars($row["timestamp"]) ?>
            </li>
          <?php endwhile; ?>
          </ul>
        <?php else: ?>
          <p>0 results</p>
        <?php endif; ?>
        
        <?php
          mysqli_close($conn);
        ?>
        

        🖖 Живіть довго і процвітайте

        --
        „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
        — @Grantscheam auf Twitter
        1. Alles klar soweit. Nur:

          Wenn man „teure“ Funktionen wie htmlspecialchars() auf einen „score“ (Integer bzw. Dezimalwert) oder Timestamp anwenden muss, die gerade aus eine Datenbank kommen, dann hat der Währunghüter(¹) was falsch gemacht.


          ¹) Person oder Organisation, die fest gelegt hat, was die Datenbank beinhaltet oder ausgibt.

          1. n'Abend,

            Wenn man „teure“ Funktionen wie htmlspecialchars() auf einen „score“ (Integer bzw. Dezimalwert) oder Timestamp anwenden muss, die gerade aus eine Datenbank kommen, dann hat der Währunghüter was falsch gemacht.

            ja, mag sein. Aber auch pure Gewphnheit könnte einen dazu verleiten, htmlspecialchars() generell beim Ausgeben von HTML anzuwenden. Vielleicht kommen ja bei anderer Gelegenheit auch wieder alphanumerische Inhalte aus der DB, die auch mal '<' oder '&' enthalten könnten.

            Es tut ja nicht weh und kostet nichts - außer Performance im ppm-Bereich.

            Einen schönen Tag noch
             Martin

            --
            "Malen nach Zahlen" sagten wir im Matheunterricht, wenn es bei der Kurvendiskussion hieß: Zeichnen Sie den Graphen der Funktion ...
        2. @@Gunnar Bittersmann

          Sieht dann so aus …

          Weiteres Optimierungspotential: ersetze alle " durch '. Die Strings müssen ja nicht nach darin vorkommenden Variablen geparst werden.

          🖖 Живіть довго і процвітайте

          --
          „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
          — @Grantscheam auf Twitter
          1. Weiteres Optimierungspotential: ersetze alle " durch '.

          2. Hallo Gunnar,

            wenn überhaupt, ist das Nanooptimierung. Und eigentlich sollte der Bedarf, Variablen in einem Literal zu ersetzen, vom PHP Parser festgestellt werden und nicht zur Laufzeit. Findet er kein $, sollte die Ausführungszeit gleich sein.

            Deshalb wundert es mich, dass Jörg da was feststellen konnte. Es sei denn, Lerdorf&Co haben mal wieder gepatzt. Verwunderlich wäre es nicht.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Lerdorf&Co haben mal wieder gepatzt.

              Gemecker 😀

              Wenn gepatzt, dann bei echo $foo, $bar, $baz;. Das scheint zu etwas echo $foo; echo $bar; echo $baz zu werden, denn es muss ja einen Grund geben, warum das so viel langsamer als das Verbinden der Strings ist.

              Wohl weil man das echo so „notlos“ wie unbedingt nicht als Funktion notiert haben wollte.

    2. Wir (hier im Forum) hatten genau das Thema schon mal (als PHP 7.2 neu war) und das Ergebnis hatte uns damals alle überrascht:

      Zum selbst Testen:

      <?php
      # 1.php
      
      $row["user"]="fritz";
      $row["score"]=34;
      $row["timestamp"]='2022-12-30';
      
      for ($i=1;$i<100000;$i++) {
      echo "user: ", $row["user"], " - score: ", $row["score"], " ", $row["timestamp"], "<br>";
      }
      
      <?php
      #2.php
      
      $row["user"]="fritz";
      $row["score"]=34;
      $row["timestamp"]='2022-12-30';
      
      for ($i=1;$i<100000;$i++) {
      echo "user: ". $row["user"]. " - score: ". $row["score"]. " ". $row["timestamp"]. "<br>";
      }
      

      Im Terminal ausgeführt (und die Ausgaben nach /dev/null umgeleitet) ist die Variante mit Konkatenierung stabil um den Faktor 3 schneller:

      time 1.php > /dev/null
      real	0m1,026s
      user	0m0,523s
      sys	0m0,472s
      
      
      time 2.php > /dev/null
      real	0m0,347s
      user	0m0,126s
      sys	0m0,186s
      

      „Unverwässert“ durch z.b. ohne Zeiten für das Starten des Interpreters (user-time), wird sogar weniger als 1/4 der Zeit gebraucht. (Getestet auf Raspi4 mit PHP 8.2.0RC7 (cli) (built: Nov 26 2022 14:19:44) (NTS) )

      Testsieger: Volles Konkatenieren des Outputs:

      <?php
      # 3.php
      
      $row["user"]="fritz";
      $row["score"]=34;
      $row["timestamp"]='2022-12-30';
      
      $out='';
      for ($i=1;$i<100000;$i++) {
      $out.="user: ". $row["user"]. " - score: ". $row["score"]. " ". $row["timestamp"]. "<br>";
      }
      echo $out;
      
      time 3.php > /dev/null
      real	0m0,197s
      user	0m0,086s
      sys	0m0,074s
      

      Letzte Mikrooptimierung: Ersparen des Nachsehens, ob in den Strings etwas ersetzt werden muss ("'):

      <?php
      # 4.php
      
      $row["user"]="fritz";
      $row["score"]=34;
      $row["timestamp"]='2022-12-30';
      
      
      $out='';
      for ($i=1;$i<100000;$i++) {
      $out.='user: '. $row['user']. ' - score: '. $row['score']. ' '. $row['timestamp']. '<br>';
      }
      echo $out;
      
      time 4.php > /dev/null
      real	0m0,192s
      user	0m0,109s
      sys	0m0,055s
      
      1. <?php
        # 5.php
        
        $row["user"]="fritz";
        $row["score"]=34;
        $row["timestamp"]='2022-12-30';
         
        for ($i=1;$i<100000;$i++):
        ?>
        'user: '<?=$row['user'];?>' - score: '<?=$row['score'];?>' '<?=$row['timestamp'];?>'<br>';
        <?php
        endfor;
        
        time 5.php > /dev/null
        real	0m0,127s
        user	0m0,044s
        sys	0m0,056s
        

        Das machte „Wusch!“. Zum Vergleich noch mal die Zahlen des langsamsten Versuchs: echo der kommagetrennten Liste:

        time 1.php > /dev/null
        real	0m1,026s
        user	0m0,523s
        sys	0m0,472s
        

        Das braucht 10 Mal so lange…

        Guten Rutsch!

        1. Das braucht 10 Mal so lange…

          Ich habe Mist gemessen. Asche auf mein Haupt.

          Ich hab versehentlich die Vorlage für das Skript getestet. Was ich also wirklich gemessen habe hat gar nichts ausgegeben.

          <?php
          # 5.php
          $row["user"]="fritz";
          $row["score"]=34;
          $row["timestamp"]='2022-12-30';
          
          for ($i=1;$i<100000;$i++):
          ?>
          'user: '<?=$row['user'];?>' - score: '<?=$row['score'];?>' '<?=$row['timestamp'];?>'<br>';
          <?php
          endfor;
          
          real	0m1,032s
          user	0m0,533s
          sys	0m0,468s
          

          Und liegt damit sogar im Bereich des schlechtesten Ergebnisses:

          time 1.php > /dev/null
          real	0m1,026s
          user	0m0,523s
          sys	0m0,472s
          

          So nebenher, bevor jemand fragt: Ein explizites implode() mit einem als Konstante definierten Trenner lag zwischen Konkatenieren (das ist schneller) und der Übergabe als Liste (ist langsamer)

          <?php
          # 6.php
          
          define ('DELIM', '');
          $row["user"]="fritz";
          $row["score"]=34;
          $row["timestamp"]='2022-12-30';
          
          $arr=[ 'user: ', $row['user'], ' - score: ', $row['score'], ' ', $row['timestamp'], '' ];
          
          for ($i=1;$i<100000;$i++) {
          echo implode( DELIM , $arr );
          }
          

          time php 6.php > /dev/null
          
          real	0m0,298s
          user	0m0,153s
          sys	0m0,108s
          
    3. @@Robert B.

      geht auch einfacher und mutmaßlich performanter, da die Einzelteile direkt in die Ausgabe geschrieben und nicht erst konkateniert werden müssen:

      echo "user: ", $row["user"], " - score: ", $row["score"],
          " ", $row["timestamp"], "<br>";
      

      Beim Schreiben dieses Postings fiel es mir wie Schuppen aus den Haaren: Es geht noch einfacher! Innerhalb von " werden Variablen ja aufgelelöst:

      echo "user: $row[user] - score: $row[score] $row[timestamp]<br>";
      

      (Über das <br> hatte ich mich ja schon geäußert. at auch.)

      Die bevorzugte sollte aber diese Variante sein (mit '). Niemals Markup per echo ausgeben, sondern nur die Daten.

      🖖 Живіть довго і процвітайте

      --
      „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
      — @Grantscheam auf Twitter
      1. echo "user: $row[user] - score: $row[score] $row[timestamp]<br>";
        

        Hm. Ja. Beide Varianten sind etwa gleich schnell und liegen (mit) vorn, aber hinter der 4. Variante (Konkatenieren aller Ausgaben)

        <?php
        # 7.php
        
        $row["user"]="fritz";
        $row["score"]=34;
        $row["timestamp"]='2022-12-30';
        
        
        for ($i=1;$i<100000;$i++) {
        echo "user: ${row['user']} - score: ${row['score']} ${row['timestamp']}<br>";
        #echo "user: $row[user] - score: $row[score] $row[timestamp]<br>";
        }
        

        time php 7.php > /dev/null
        
        real	0m0,278s
        user	0m0,109s
        sys	0m0,141s
        

        Niemals Markup per echo ausgeben, sondern nur die Daten.

        Sag niemals „niemals“. 8.php = mix( '4.php', '7.php' )

        <?php
        # 8.php
        
        $row["user"]="fritz";
        $row["score"]=34;
        $row["timestamp"]='2022-12-30';
        
        $out='';
        for ($i=1;$i<100000;$i++) {
        $out.="user: ${row['user']} - score: ${row['score']} ${row['timestamp']}<br>";
        }
        echo $out;
        

        time php 8.php > /dev/null
        
        real	0m0,181s
        user	0m0,081s
        sys	0m0,073s
        

        ... das geht bis jetzt am schnellsten und erzeugt ergo am wenigsten Kohlendioxyd. Es kann also eine Frage der Präferenz bzw. Zielsetzung sein.

        1. SELECT
              CONCAT (
                  "user: ",
                  `user`,
                  " - score: ",
                  `score`,
                  " ",
                  `timestamp`
              ) AS `row`
          
          FROM `myTable`;
          

          (Syntax für mysql/mariadb)

          Das geht freilich nur, wenn die Daten nicht auch noch anderweitig verarbeitet werden sollen und kann nachteilig sein, weil im Ergebnis mehr Daten über das langsame Netzwerk zurückgeliefert werden, ist sogar definitiv nachteilig, weil ein Teil des Outputs sogar im SQL notiert werden muss, was den Code schlechter pfleg- oder wiederverwendbar macht. Also nur testen und ggf. dann machen, wenn es wirklich schneller ist und aus $Gründen auf die Performance ankommt.

          1. Hallo Raketenwilli,

            je nach dem, wie oft du das abfragst, kann sich sogar eine materialisierte, berechnete Spalte dafür lohnen und 2-3µg CO₂ pro Query einsparen

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Willkommen im neuen Jahr!

              je nach dem, wie oft du das abfragst, kann sich sogar eine materialisierte, berechnete Spalte dafür lohnen und 2-3µg CO₂ pro Query einsparen

              Nun ja ... ich selbst bin ja eher ein Freund des „Hardcore-Cachings“. Da scheinbar (siehe SQL-Abfrage ohne WHERE-Klausel) die gesamte Datentabelle ausgeliefert wird, bietet es sich ohnehin an, das auszuliefernde HTML sogar schon gepackt bereit zu halten. Man lösche einfach die gespeicherte Cache-Datei sobald in der Datentabelle Änderungen gemacht wurden und erzeuge diese Datei also beim nächsten Abruf neu - wenn und nur falls kein Cache vorhanden ist.