Jörg: php8: Fatal error: Uncaught Error: Undefined constant "int" in...

Hallo Forum,

ich muss bis Mitte November meine Anwendung auf php8 umstellen. Wird noch schwierig werden, weil ich einige Fremdscripte darin nutze, die ich "gepimped" habe und gar nicht mehr genau weiß, wo und wie. Kann also nicht einfach die neueste version der Fremdscripte nutzen.

Ok, aber jetzt mal zum aktuellen Thema:

Ich habe an wirklich unzähligen Stellen in unzähligen Scripten Funktionen aufgerufen, in denen ich einen String-Parameter nicht in Anführungszeichen gesetzt habe, also sowas wie myfunct($bla,int);.
Folglich erhalte ich in php8 nun den fatalen Fehler "Uncaught Error: Undefined constant "int" in...".

Spricht eigentlich irgendwas dagegen, entsprechende Konstanten einfach anzulegen und so dem Schlamassel zu entgehen, alle Scripte nach allen Stellen zu durchsuchen, in denen diese Vorgehensweise moniert werden würde?

Oder gibts nen anderen Trick, mit dem ich die mühevolle Arbeit vermeiden kann?

Gruß, Jörg

  1. Hallo Jörg,

    Oder gibts nen anderen Trick, mit dem ich die mühevolle Arbeit vermeiden kann?

    Nein, einen anderen Trick gibt es nicht.

    Du hast insofern Glück, dass PHP einiges seiner früheren Dummheit abgelegt hat.

    Dieser Code wird tatsächlich ausgeführt und mault nicht, dass in der Deklaration von test ein "FOO" vor dem $x steht. Er erkennt die Typdeklaration und ersetzt sie nicht durch die Konstante.

    define ("int", "FOO");
    
    function test(int $x, $y) {
       return "Das ist $y - $x";
    }
    
    echo test(3, int);
    

    Aber ist das lesbar? Hat das Zukunft? NEIN! Da musst Du durch. Strings ohne Anführungszeichen sind ein Erbe aus PHP 4 Zeiten und werfen seit PHP 5.2 eine Notice. Seit PHP 7.2 eine Warnung mit der deutlichen Ankündigung des Errors.

    Wenn Du einen Zwangstermin im November hast, kannst Du Dir mit dem define Luft verschaffen. Ich kann aber nicht garantieren, dass der überall das tut, was Du erwartest.

    Mit einem guten Editor könntest Du einen projektweiten search&replace über alle Dateien probieren, bei dem Du jeden Treffer von Hand bestätigen musst. Du könntest dann dem Editor sagen, dass er nur das Wort int mit Rücksicht auf Groß-/Kleinschreibung durch "int" ersetzen solle. Besser noch wäre eine Konstante J_TYPE_INT (oder wie auch immer deine Konvention für selbstdefinierte Konstanten aussieht), die Du mit define auf "int" festlegst. Denn "int" ist ein magischer String - und Du merkst gerade, welche Konsequenzen dieser faule Zauber hat.

    Ab PHP 8.1 kannst Du statt selbstdefinierter Konstanten auch Enumerationen verwenden. Das ist dann zwar etwas mehr Schreibarbeit, aber dass die Würze der Kürze schnell fad werden kann, merkst Du ebenfalls soeben.

    Enums:

    <?php
    enum Foo {
    	case Dings;
    	case Bums;
    }
    
    function test(Foo $f) {
    	echo $f->name;
    }
    
    test(Foo::Dings);   // Läuft
    test("Dings");      // Error
    

    Sowas sind technische Schulden. Das Gemeine an PHP ist, dass etliche Dinge, die früher mal ganz normale Praxis waren, auf einmal zu technischen Schulden wurden. Das liegt unter anderem daran, dass die frühen PHP Versionen schludrig konzipiert waren und man das seit vielen Jahren Schritt für Schritt abbaut.

    Technische Schulden muss man irgendwann abtragen. Je mehr man sich damit Zeit lässt, desto teurer wird es. Aber das Problem geht nicht weg. Es sei denn, du bist in der glücklichen Lage, selbst wegzugehen und das Problem einem Nachfolger aufzuhalsen.

    Das Auffinden der Probleme wird ggf. durch eine gute Entwicklungsumgebung vereinfacht. Oder du rufst den in PHP eingebauten Linter manuell für alle deine Sourcen auf:

    https://www.php.net/manual/en/features.commandline.options.php

    Rolf

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

      erstmal danke für die hilfreichen Erklärungen.
      Und ja, ich nachvollziehe, dass ich nicht zwingend etwas für diese "Fehler" kann, aber sie nun dennoch korrigieren muss.

      Wie schon erwähnt, sind diese Fleissaufgaben auch sicher nicht mein Hauptproblem, wenngleich ich heute schon sicher bin, dass ich gar nicht alle Funktionen meiner Anwendung selber ausprobieren kann, weshalb dann später mal (wie so oft) der User selber den Rest finden wird.

      Mit einem guten Editor könntest Du einen projektweiten search&replace über alle Dateien probieren, bei dem Du jeden Treffer von Hand bestätigen musst. Du könntest dann dem Editor sagen, dass er nur das Wort int mit Rücksicht auf Groß-/Kleinschreibung durch "int" ersetzen solle.

      Ist php-Storm ein "guter Editor"? Ich wüßte nicht, wie ich dem das beibringen kann.

      Gruß, Jörg

      1. Hallo Jörg,

        Ist php-Storm ein "guter Editor"?

        Ich weiß es nicht, ich verwende VS Code, bzw. früher einfach Notepad++. In denen geht das.

        Aber Jetbrains ist ein reputabler Hersteller, phpStorm kann das garantiert auch. Erster Treffer bei Google

        Rolf

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

          Aber Jetbrains ist ein reputabler Hersteller, phpStorm kann das garantiert auch. Erster Treffer bei Google

          Du hast recht, ich habe mich da nicht heran getraut, weil ich weiß, dass das wieder auf Reguläre Ausdrücke hinaus laufen wird.

          Und genau so ist es auch. Über "Strg+Shist+R" kann man in php-Storm auch projekt- oder verzeichnissbezogen suchen und ersetzen.

          Da ich aber noch Konstrukten ala myfunct($string,int) suchen muss und in den regex-pattern nicht wirklich firm bin, trau ich mich da nicht ran.

          Ich müsste quasi suchen nach:

          • dem String myfunct
          • gefolgt ggf. von Leerzeichen oder auch nicht
          • dann Klammer auf (
          • dann ein Dollarzeichen $
          • dann ein beliebiger String (also in Verb. mit dem Dollarzeichen meine php-Variable)
          • dann Leerzeichen oder auch nicht
          • dann ein Komma ,
          • dann Leerzeichen oder auch nicht
          • dann der String int
          • dann Leerzeichen oder auch nicht
          • und dann Klammer zu )

          Hoffe, ich habe nichts vergessen.

          Nächstes Problem, selbst wenn ich die Regex hätte, weiß ich nicht, wie ich php-Storm beibringe, alles beizubehalten, wie es ist und nur anstelle von int nun daraus 'int' zu machen.

          Jetbrains search and replace

          Vielleicht kennt sich ja einer mit Php-Storm, AndroidStudio oder anderen Jetbrains-Produkten aus, die dürften das sicher alle ähnlich machen.

          Jörg

          1. Hallo Jörg,

            MUSST du das per Regex machen? Musst Du nicht vielmehr nach dem Wort int suchen?

            Wenn ich das im Screenshot bei JB richtig sehe, gibt's im Suchfeld den Suchtext, ein "Aa" und ein "W". Ich würde unterstellen, dass "Aa" bedeutet, dass er case-sensitive suchen soll und "W", dass er ganze Worte suchen soll. Das ".*" bedeutet Regex - aber das kannst Du dafür auslassen.

            Wenn Du unbedingt Regex willst (ich weiß, willst Du nicht), könntest Du mit \b arbeiten, das ist "Word Boundary": Eine Suche nach \bint\b würde ebenfalls ganze Worte suchen.

            Wenn Du auf diese Weise int durch "int" ersetzt, musst Du auf den Funktionskontext nicht mehr achten.

            Eine Regex für Funktionsaufrufe ist nämlich eine heikle Sache, denn Regex ist für diese Aufgabe eigentlich eine Nummer zu klein. Man braucht einen Parser.

            foo(3, int) ist unproblematisch.

            Aber was ist mit foo(bar(7, "huhu"), int)? Das Finden der schließenden rechten Klammer zu foo( ist dann nicht mehr so einfach.

            Und bist Du sicher, dass Du dieses int nur im Kontext eines Funktionsarguments verwendest? Könnte auch sowas hier passieren?

            $format = int;
            $wert = 12.3;
            
            tuwas($wert, $format);
            

            Die Suche nach dem Wort int ist da deutlich einfacher.

            Die andere Frage ist, welche "bösen Wörter" Du außer int noch im Sourcecode hast. Wenn phpStorm so gut ist, wie JetBrains behauptet (bei 90€/Jahr sollte er das sein), dann müsste es einen Linter geben, der Dir eine Codeanalyse macht und über das ganze Projekt alle Fehler dieser Art ausspuckt.

            Rolf

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

              MUSST du das per Regex machen? Musst Du nicht vielmehr nach dem Wort int suchen?

              Naja, m eine Hoffnung auf "replace_all" stirbt zuletzt, hm? 😉

              Wenn ich das im Screenshot bei JB richtig sehe, gibt's im Suchfeld den Suchtext, ein "Aa" und ein "W". Ich würde unterstellen, dass "Aa" bedeutet, dass er case-sensitive suchen soll und "W", dass er ganze Worte suchen soll. Das ".*" bedeutet Regex - aber das kannst Du dafür auslassen.

              Alles korrekt unterstellt.

              Nur... wenn ich "W" anhake, dann wird auch 'int' gefunden und genau das birgt dann wieder große gefahren, wenn ich die dann zu doppelten ''int'' austausche.

              foo(3, int) ist unproblematisch.

              Trotz ggf. leerzeichen oder auch nicht?

              Aber was ist mit foo(bar(7, "huhu"), int)? Das Finden der schließenden rechten Klammer zu foo( ist dann nicht mehr so einfach.

              Nein, sowas habe ich nie verwendet, da bin ich sicher.

              Und bist Du sicher, dass Du dieses int nur im Kontext eines Funktionsarguments verwendest? Könnte auch sowas hier passieren?

              $format = int;
              $wert = 12.3;
              
              tuwas($wert, $format);
              

              Nein, sowas habe ich auch nie verwendet, da bin ich auch sicher.

              Die Suche nach dem Wort int ist da deutlich einfacher.

              Findet aber, wie schon gesagt, die falschen Sachen, da 'int' auch gefunden wird.

              Jörg

              1. Hallo Jörg,

                Nur... wenn ich "W" anhake, dann wird auch 'int' gefunden und genau das birgt dann wieder große gefahren, wenn ich die dann zu doppelten ''int'' austausche.

                Jaaa - sowas hatte ich auch schonmal.

                Ich habe einen üblen Workaround verwendet.

                (1) Maskiere die Fehltreffer, indem Du sie durch etwas anderes ersetzt, das definitiv an keiner Stelle in deinem Projekt vorkommt und was von int nicht gematcht wird. Z.B. 'int' durch 'in@@t'. Das solltest Du dann auch mit vorhandenen Inkarnationen von "int" tun.

                (2) Überzeuge Dich, dass Du keine falschen Treffer mehr für int hast. Das kann lästig sein, ich weiß. Aber eine Fehlermeldung wie "Der Wert muss int sein" willst Du sicherlich nicht ersetzen.

                (3) Ersetze int durch "int"

                (4) Ersetze zurück: 'in@@t' durch 'int' und "in@@t" durch "int".

                Und vermutlich ist nicht nur int zu ersetzen, sondern auch string oder float oder date, gelle? Das wird SPAẞIG (und nicht etwa SPAßIG oder SPASSIG)...

                Rolf

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

                  Ich habe einen üblen Workaround verwendet.

                  Übel, aber auch eine gute Idee. 😀

                  Und vermutlich ist nicht nur int zu ersetzen, sondern auch string oder float oder date, gelle? Das wird SPAẞIG (und nicht etwa SPAßIG oder SPASSIG)...

                  Genau so sieht es aus. 😕 Und das ist, wie schon erwähnt, nur der "lästige" Part.

                  Der schwierigere:

                  Derzeit versuche ich z.b. ein PDF zu erstellen, erhalte eine weiße Seite und keinen Eintrag im Errorlog.

                  Weiß grad gar nicht, wo ich da ansetzen soll.

                  Jörg