mathefritz: php Fatal erorr Can't use function return value in write context

die durch das COOKIE "file" gegebenen Datei
enthält
Zeilen deren erstes Zeichen entweder 0 oder 7 ist, auf das eine beliebige String folgt;
in
jener Zeile, deren "beliebige String" gleich der durch das COOKIE "id" gegebenen ist
soll
das erste Zeichen wenn 0 zu 7, wenn 7 zu 0 werden.
Der
Code wird mit Javascript window.open(..) aus einer anderen phpGeneriertenDatei aufgerufen.
( die Viewportangabe erscheint mir hier überflüssig );

<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><?php

if ( ! rename(($Fname = $_COOKIE["file"]),"old")
   ) exit("Umbenennung scheiterte");

$ChangeOnId = $_COOKIE["id"] . "\n";

$oldFile = fopen("old",'r'); $file = fopen($Fname,"w");

while( $chckdIid = fgets($oldFile) ){

       $chckdIid = ( substr($chckdIid,1) == $ChangeOnId )
                   ?
                   (substr($chckdIid,0,1) ^= 7) . $ChangeOnId // line 14
                   :       $chckdIid;
       fwrite($file,       $chckdIid);
}
fclose($file); fclose($oldFile); unlink("old");
?>
   <script> window.close();
  </script>
</body>
</html>

meldet:
Fatal error: Can't use function return value in write context in ... on line 14

  1. Tach!

    Fatal error: Can't use function return value in write context in ... on line 14

    Der relevante Code dazu ist substr($chckdIid,0,1) ^= 7. Die Meldung ist gerechtfertiget, denn man darf einem Funktionsergebnis keinen Wert zuweisen. ^= ist ein Zuweisungsoperator. Vielleicht wolltest du ja was anderes formulieren.

    dedlfix.

    1. Danke;
      autsch, da bin ich ja glimpflich davongekommen, natürlich ist keine Zuweisung gemeint. Einfach das "=" weglasen und klappt.

      1. "schwere Geburt" war allerdings auf diese $chckdIid[0] = chr( 7 ^ ord($chckdIid[0]) )
        Lösung für das Ändern des einen Zeichens zu kommen; die anderen verschluckten mir immen de Umbruch.

        1. Ich denke, dein Code ist nicht sehr robust. Und schwer lesbar finde ich ihn auch.

          Ich tät's so aufschreiben:

          while ( ($chckdIid = fgets($oldFile)) !== false ) {
             if (substr($chckdIid, 1) === $ChangeOnId) {
                $chckdIid = ($chckdIid[0] === "0" ? "7" : "0") . $ChangeOnId
             }
             fwrite($file, $chckdIid);
          }
          

          Und zwar aus diesen - möglicherweise subjektiven - Gründen:

          fgets liefert false, wenn EOF erreicht ist. Es gibt aber auch noch andere Werte, die falsy sind, wie z.B. Leerstring oder der String "0". Das wird Dir bei fgets (glaube ich) nicht passieren, aber ich finde, man sollte sich gerade bei diesem Designirrtum namens PHP API nicht auf truthy oder falsy verlassen, sondern explizit auf false abfragen.

          Vergleich mit == führen in PHP erstmal zum Versuch, die Typen anzugleichen. Wenn das nicht nötig ist, oder ggf schädlich sein könnte, nehme ich immer === und !==, die setzen Typgleichheit voraus.

          Auf eine Stelle eines Strings muss man übrigens nicht mit substr zugreifen, da geht auch []. Einen Fehler wegen Indexzugriff hinter's Stringende kannst Du in diesem Fall nicht bekommen, weil sonst vorher schon der Vergleich mit $ChangeOnId falsch ist.

          Die Bitmanipulation, um zwischen 0 und 7 umzuschalten, sieht nach einer cleveren Lösung aus, ich halte es aber für eine nicht empfehlenswerte Praxis, die numerische Codierung eines Zeichens ohne Not als bekannt vorauszusetzen. Und er muss dafür unter der Haube eben mal zwischen String -> Int -> String hin und her jonglieren.

          Rolf

          Nachtrag: Warum machst Du das eigentlich mit window.open()? Du kannst ein PHP Script auch als XMLHttpRequest aufrufen, also Ajax. Da brauchst Du dich nicht einmal auf die Antwort zu registrieren, die interessiert Dich sowieso nicht. Spricht da was gegen?

          1. Danke Rolf!

            ... . Es gibt aber auch noch andere Werte, die falsy sind, ...

            die Dateien sind von mir selbst erzeugte; gute Gewohnheiten sind natürlich hilfreich .

            Vergleich mit == führen in PHP erstmal zum Versuch, die Typen anzugleichen.

            damit hatte ich wohl zu kämpfen ( verschluckter Umbruch )

            Auf eine Stelle eines Strings muss man übrigens nicht mit substr zugreifen, da geht auch [].

            ja, habe mich in späterer Version teilweise daran erinnert es irgendwo gelesen zu haben - aber im wiki und php.net nicht .

            Nachtrag: ... XMLHttpRequest ...

            interessant: für mich dann also XMLHttpRequest.open(...); das funktionier dann hofentlich auch über $_GET[]; mit window.open klappte es nicht, daher cookies . Mit Ajax hab ich mich noch nicht befaß . Ist in dem speziellem Fall Vertiefung nötig?

            Gruß Fritz.

          2. Nachtrag: while ( ($chckdIid = fgets($oldFile)) !== false ) { oder auch === true ?

            1. fgets liefert einen string oder false. Also nur !== false. === true wird niemals zutreffen.

              Rolf

          3. und jetzt?

            1. Wozu haben wir unser Wiki, wenn es niemand liest...

              Die Header im Wiki-Beispiel kannst Du weglassen, die sind nur drin, um zu zeigen, wie man welche setzt wenn man sie unbedingt haben will.

              Rolf

              1. Danke;
                ok, als Minimalbeispiel habe ich folgendes versucht:
                die
                3 Dateien JsSender.hp, empfngr.php, orig
                befinden
                sich alle im selben Ordner.

                empfngr.php soll die Kopie copy, die noch nicht existiert,
                aus orig herstellen wenn JsSender.php gebrowst wird
                klappt
                abgesehen davon daß das echo ausbleibt .
                JsSender.php:

                <html><head><title>XMLHttRequest</title><meta charset="utf-8">
                      <style></style>
                    <script>
                     function cp(from,to){
                
                       var rq =  new XMLHttpRequest();
                
                       var cmd = "empfngr.php?src="+from+"&dst="+to;
                
                       rq.open("GET",cmd, async = false); rq.send();
                     }
                   </script>
                </head>
                  <body><script> cp("orig","copy");
                       </script>
                </body>
                </html>
                

                empfngr.php:

                <?php
                 echo "Da bin ich " . $_GET['src'] . " " . $_GET['dst'] . "<br>";
                 copy( $_GET["src"] , $_GET["dst"]);
                ?>
                

                Meldungen in der Debuger Konsole:

                Alternativ-Text Alternativ-Text Alternativ-Text Alternativ-Text Alternativ-Text

        2. unpack("C*", $zeile); liefert Dir ein Array mit numerischen Werten, da wäre nur noch zu vergleichen, ob das erste Arrayelement integer 48 oder 55 ist. Der Rest ist ein Stringvergleich.

          So einfach kanns gehn 😉

          1. Nee, Namenskollege, da bin ich nicht bei Dir.

            $zeile[0] === "0"

            unpack("C*", $zeile)[1] === 48;

            Preisfragen: Was ist lesbarer? Was ist effizienter?

            Rolf

            1. Die Oktettenwertigkeit numerischer Werte ergibt immer true. Das wäre ein Motiv, es so zu machen, das hast Du ja selbst bereits festgestellt. Und wie schon so oft gesagt: Es gibt Numerische Datentypen, nicht nur in PHP, c, Java usw., auch in Perl sind sie vorhanden.

              D.h., dass eine Zahl nicht als 123-String in einer Datei zu stehen hat, sondern als eine dem Datentype entsprechende Bytefolge. Das funktioniert sogar mit abstrakten Datentypen.

              Mit solch einer Logik erklärt sich jeder Code von selbst und das Problem Lesbarkeit ist damit auch gelöst.

              MfG

              --
              Der schwarzen Spechte Lieblingsspeise ist die Rote Waldameise.
              1. als "alter Hase" kann ich nicht umhin mich - einwenig - um RAM-Bedarf und - vielleicht etwas mehr - um Garbagecollection Aufwand zu sorgen .

                1. Es ist immer wieder interessant, bei den alten Meistern (Kernighan, Ritchie, Wirth.…) mal nachzuschlagen anstatt das Rad neu zu erfinden zu wollen 😉

                  MfG

                  PS: serialize() ist als Algorithmus ziemlich ineffizient, und genau das liegt daran, dass mit diesem Algorithmus vehement mit dem Sinn Numerischer Datentypen gebrochen wird. Aber bei kleinen Dateien merkt das ja eh keiner 😉

              2. Hallo,

                Mit solch einer Logik erklärt sich jeder Code von selbst und das Problem Lesbarkeit ist damit auch gelöst.

                Geil! Nie wieder langweilige Kommentare im Code und die Dokumentation ist damit auch überflüssig!

                Gruß
                Kalk

                1. Na, wenn der Kommentar nicht zum Code passt, kann ja auch der Code falsch sein.

                  1. Hallo,

                    Na, wenn der Kommentar nicht zum Code passt, kann ja auch der Code falsch sein.

                    und wenn das Antwortposting nicht zum vorigen passt, kann ja auch…

                    Gruß
                    Kalk

                    1. und wenn das Antwortposting nicht zum vorigen passt, kann ja auch…

                      daran liegen, dass Du's nicht verstanden hast. Du bist doch lange genug dabei oder? Natürlich kann man sich seinen eigenen Serializer, JSON-, XMLParser usw. schreiben, aber dann sollte man schon genau wissen was man tut. Und vor Allem wissen warum man es tut. Wenn serialize() i:-255 in eine Datei schreibt, ist das noch lange kein integer nur weil i: davor steht, sondern es ist und bleibt ein String. Genausowenig macht das Weglasssen der Anführungszeichen aus "123" eine Zahl 123.

                      Das passiert ja nur deswegen, weil PHP oder Perl das so interpretiert, daher auch der Ausdruck Interpreter. Datentypen gibt es seit Urzeiten und deren Speicherbedarf ist genauso festgelegt wie die dazugehörigen Bytesequenzen. Eben das ist es, was maschinenlesbare Dateien ausmacht und das war schon zu Zeiten der Lochkarten so.

                      Das Wichtigste beim Programmieren ist eine abstrakte Denkweise. Dann passen auch die richtigen Antworten zu den falschen Fragen.

                      MfG

                      1. Tach!

                        Wenn serialize() i:-255 in eine Datei schreibt, ist das noch lange kein integer nur weil i: davor steht, sondern es ist und bleibt ein String. Genausowenig macht das Weglasssen der Anführungszeichen aus "123" eine Zahl 123.

                        Das passiert ja nur deswegen, weil PHP oder Perl das so interpretiert, daher auch der Ausdruck Interpreter.

                        Natürlich, und jede Programmiersprache, mit der man in der Lage ist, serialisierte Daten zu lesen, nennt man Interpreter. Alles klar.

                        dedlfix.

                        1. Hallo,

                          Natürlich, und jede Programmiersprache, mit der man in der Lage ist, serialisierte Daten zu lesen, nennt man Interpreter. Alles klar.

                          Nein, eigentlich meint pl, jede Programmiersprache in der man die Kommentare weglässt, weil man selber den Code interpretieren kann, sei ein Interpreter…

                          Gruß
                          Kalk

                          1. Nein, ich meine, dass jeder seinen eigenen Style finden sollte wie bei jedem anderen Handwerk auch. Und wenn einem typegerechten Vergleich eine typegerechte Speicherung vorausging, ist das zwar auch nichts Besonderes, aber ein Style der von einem soliden Grundwissen zeugt und von der Fähigkeit, abstrakte Denkweisen praktisch anwenden zu können.

                            Schöne Grüße!

                        2. Ne, gar nichts ist Dir klar. Wenn Du typegerecht vergleichen willst, dann kannst Du nämlich auch typegerecht speichern.

                          MfG

                      2. Datentypen gibt es seit Urzeiten und deren Speicherbedarf ist genauso festgelegt wie die dazugehörigen Bytesequenzen.

                        Dass man in den meisten Programmiersprachen den Speicherbedarf und die konkrete Binärdarstellung von Zahlentypen festlegt ist eine rein praktische Entscheidung. Die Befehlssätze von Prozessoren sind dafür ausgelegt mit Binärzahlen fester Länge zu rechnen.

                        Das heißt aber nicht, dass unsere Computer ausschließlich mit Maschinenzahlen rechnen können. In der Theorie existiert so eine Beschränkung nicht. Man kann auch Programme schreiben, die mit den „echten“ abstrakten Zahlen, die uns aus der Mathematik bekannt sind, rechnen; also zum Beispiel die natürlichen Zahlen ℕ, die rationalen Zahlen ℚ, oder die komplexen Zahlen ℂ. Für die exakten Rechnungen mit diesen Datentypen stehen uns aber keine direkten Prozessor-Befehle zur Verfügung. Die Algorithmen für die Grundrechenarten müssen also Software-seitig programmiert werden und sind komplexer als die Hardware-Lösungen.

                        Das ist eine Tradeoff-Entscheidung: Rechnen mit abstrakten Zahlen ist (unvermeidbar) langsamer als das Rechnen mit Maschinenzahlen, aber dafür sind die Rechnungen exakt und nicht nur Annäherungen an die gewollte Lösung.

                        Für das exakte Rechnen mit abstrakten Zahlen ist es nicht erforderlich und auch gar nicht möglich vorher ein Hardware-nahes Speicherlayout festzulegen. Stattdessen muss man wissen, wie man Zahlentypen sofwareseitig kodiert. Die theoretischen Grundlagen dafür haben uns Alonzo Church und seine Studenten mit dem sogenannten Church Encoding bereitet. Auf Churchs Arbeit basiert übrigens auch unsere moderne Auffassung von abstrakten Datentypen. Haskell ist eine Programmiersprache, die im wesentlichen genau auf dieser Theorie beruht und mit abstrakten Zahlen rechnen kann.

                        Das Wichtigste beim Programmieren ist eine abstrakte Denkweise.

                        Du versuchst Abstraktes in eine konkrete Form zu pressen - an diesem Beispiel wird das besonders deutlich: Du reduzierst mathematische Operationen auf ihr Speicherlayout. Dabei gehen aber Eigenschaften verloren, die im abstrakten Setting gelten und im konkreten nicht. Du wendest selber noch nicht an, was du als das "Wichtigste beim Programmieren" bezeichnest.

                        1. Dass man in den meisten Programmiersprachen den Speicherbedarf und die konkrete Binärdarstellung von Zahlentypen festlegt ist eine rein praktische Entscheidung.

                          Bingo! Das heißt, dass numerische Datentypen gar keinen expliziten Serializer brauchen wenn es darum geht, diese in Dateien abzulegen. Die c-Philosophie geht sogar noch ein Stück weiter, was das Speichern abstrakter Datentypen betrifft, wie die einfache Anwendung von fwrite() und fread() zeigt.

                          MfG

                          1. Moin Rolf,

                            dein Ansatz mag im konkreten Fall auf einer Intel-CPU funktionieren, aber es ist nicht allgemein anwendbar, weil das Speicherlayout eines komplexen Datentypen in C plattformabhängig ist. Das fängt schon beim Unterschied zwischen Little und Big Endian und hört bei Strings als char-Pointern nicht auf.

                            Viele Grüße
                            Robert

                          2. Dass man in den meisten Programmiersprachen den Speicherbedarf und die konkrete Binärdarstellung von Zahlentypen festlegt ist eine rein praktische Entscheidung.

                            Bingo! Das heißt, dass numerische Datentypen gar keinen expliziten Serializer brauchen wenn es darum geht, diese in Dateien abzulegen.

                            Maschinenzahlen könntest du genauso kodieren, wie sie im Hauptspeicher kodiert sind - ja. Das will man aber oft nicht, weil das Format nicht menschenlesbar, nicht speichereffizient und auch nicht platformübergreifend ist. Für bestimmte Anwendungsfälle mag es trotzdem die richtige Wahl sein - Debugging zum Beispiel. Andere Anwendungsfälle verlangen andere Kodierungen.

                            Und Nicht-Maschinenzahlen haben überhaupt keine feste Repräsentation im Hauptspeicher, sie werden im Programm kodiert. Die natürlichen Zahlen in Church-Kodierung sähen in JavaScript zum Beispiel so aus:

                            const Zero = x => x;
                            const Successor = f => x => f(x);
                            

                            Diesen Fall lässt dein Vorschlag völlig unbeantwortet. Was auch okay ist, denn meistens kommt man mit Maschinenzahlen ja schon aus. Allerdings benutze ich die natürlichen Zahlen hier ja nur als Beispiel, für Datenstrukturen, die im Programm kodiert werden und die keine ablesbare Repräsentation im Hauptspeicher haben. Und dieser Umstand trifft nun mal auf die überwältigende Mehrheit der Datenstrukturen zu, mit denen wir heute täglich arbeiten. Dein Lösungsvorschlag erscheint mir deshalb zu schmalfüßig.

                            Die c-Philosophie geht sogar noch ein Stück weiter

                            C ist eine maschinennahe Sprache und als Medium zum Transport menschlicher Gedanken eher ungeeignet. Das Typsystem dient einerseits der Fehlervermeidung und andererseits der Modellierung des Speicherlayouts. Das erste hilft auch dem Menschen beim Verstehen des Programmcodes, das zweite belastet das kognitive Bewusstsein mit maschinellen Details. Du machst es dir unnötig schwer, wenn du versuchst abstrakte Konzepte anhand von C zu verstehen. Gerade im Web sind wir mit viel höheren Programmiersprachen umwöhnt, in denen wir unsere Ideen viel besser ausdrücken können, weil wir irrelevante Dateils (wie das Speicherlayout) ignorieren können.

                    2. Hallo Tabellenkalk,

                      Na, wenn der Kommentar nicht zum Code passt, kann ja auch der Code falsch sein.

                      und wenn das Antwortposting nicht zum vorigen passt, kann ja auch…

                      Manche Leute sind verwirrt, wenn ein Satz anders endet als man Kartoffelsalat.

                      Bis demnächst
                      Matthias

                      --
                      Rosen sind rot.