PHP: kleines Problem in 7.3 mit . + –

Da meldet sich doch mein Kalender heute mit der Meldung, er könnte den Termin nicht modifizieren, weil der Server einen 500er Fehler produziert hat. Der Aufruf des Webinterfaces verlief zunächst einmal ohne Befund.

PHP 8 ist bald da!

Der eigentliche Fehler im Script ist eigentlich ganz einfach, hat aber große Auswirkungen auf PHP Programme. Ich wage sogar die These, daß dieser Fehler in PHP 8 zu einem Y2K Problem wird: Fixbar, aber es wird trotzdem knallen.

Kleine Einführung, damit Sie dem auch folgen können:

PHP ist eine Programmiersprache die auf Webservern zum Einsatz kommt.

E= 3+1  ergibt E=4  ( Addition )
E= 3-1    ergibt E=2  ( Subtraktion )
„Hallo“ . “ “ . „Welt“  ergibt in PHP „Hallo Welt“
„Obj->F“ meint, Zugriff auf ein FELD (F) im Objekt (Obj) , wird in der Objektorientierten Programmierung von PHP benutzt. „$this->“ meint dabei das Objekt in dessen Kontext die Operation ausgeführt wird, sprich „meint sich selbst“.

„+-“ sind nummerische Operationen,  „.“ ist eine lexikalische Anweisung auf Textblöcke in PHP.

Schauen wir uns erstmal den Fehler an, den SabreDAV hier als Kalenderbackend produziert:

[Wed Jan 08 16:03:28.700531 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] PHP Fatal error: Uncaught ErrorException: The behavior of unparenthesized expressions containing both ‚.‘ and ‚+’/‘-‚ will change in PHP 8: ‚+’/‘-‚ will take a higher precedence in /home/<username>/cal/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php:829: <cgiwrapper>
[Wed Jan 08 16:03:28.700685 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] Stack trace:: <cgiwrapper>
[Wed Jan 08 16:03:28.700946 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #0 /home/<username>/cal/vendor/composer/ClassLoader.php(386): exception_error_handler(): <cgiwrapper>
[Wed Jan 08 16:03:28.701145 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #1 /home/<username>/cal/vendor/composer/ClassLoader.php(386): include(): <cgiwrapper>
[Wed Jan 08 16:03:28.701369 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #2 /home/<username>/cal/vendor/composer/ClassLoader.php(278): Composer\\Autoload\\includeFile(): <cgiwrapper>
[Wed Jan 08 16:03:28.701539 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #3 [internal function]: Composer\\Autoload\\ClassLoader->loadClass(): <cgiwrapper>
[Wed Jan 08 16:03:28.701741 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #4 /home/<username>/cal/lib/Sabre/CalDAV/Backend/PDO.php(520): spl_autoload_call(): <cgiwrapper>
[Wed Jan 08 16:03:28.702034 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #5 /home/<username>/cal/lib/Sabre/CalDAV/Backend/PDO.php(461): Sabre\\CalDAV\\Backend\\PDO->getDenormalizedData(): <cgiwrapper>
[Wed Jan 08 16:03:28.702503 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #6 /home/<username>/cal/lib/Sabre/CalDAV/CalendarObject.php(96): Sabre\\CalDAV\\Backend\\PDO->updateCalendarObject(): <cgiwrapper>
[Wed Jan 08 16:03:28.702713 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #7 /home/<username>/cal/lib/Sabre/DAV/Server.php(888): Sabre\\CalDAV\\CalendarObject->put(): <cgiwrapper>
[Wed Jan 08 16:03:28.703037 2020] [cgid:error] [pid 15227:tid 140702835001088] [client 83.246.80.131:60598] #8 [internal function]: Sabre\\DAV in /home/<username>/cal/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php on line 829: <cgiwrapper>

Da wird also die fehlende Klammerung von Operationen mit „.“ „-“ oder „+“ angekreidet. Erstmal komisch, bis man den Sourcecode dazu sieht:

$this->currentDate->modify(‚+‘ . $this->interval-1 . ‚ weeks‘);

PHP 7.3 meckert hier also darüber, daß der Entwickler davon ausgeht, daß eine mathematische Operation Vorrang vor einer lexikalischen Operation hat. Vom Ablauf steht dort in etwa:

$TVAR = $this->interval
$TVAR = $TVAR – 1
$TSTRING1 = StringAusZahl( $TVAR )
$TSTRING2 = FügeStringsZusammen( „+“, $TSTRING1, “ weeks“ )
RufeFunktionAuf(  $this->currentDate->modify , $TSTRING2 )

Das geht nur, weil der Operator „-“ hier Vorrang hat im Parser. Wenn das nicht mehr der Fall ist, also „.“ gleichrangig mit „-“ wäre, dann knallts, weil dann käme das raus ( oder noch etwas völlig anderes ):

$TVAR = $this->interval
$TVAR = $TVAR – „1 weeks“

Und hier wäre es dann auch schon vorbei, weil das nicht geht. Man kann keinen String von einer Zahl abziehen. Deswegen muß man jetzt eine Klammerung vornehmen, die eindeutig macht, was da wie subtrahiert werden soll und was dann als Zahl behandelt werden soll.

IMHO wäre das schon ein ParserBug in PHP 0.1 gewesen, der es nie in die Stable von PHP hätte schaffen dürfen. Das Parserverhalten jetzt in PHP 7.3 zu ändern bricht ziemlich hart mit vorhandenem Code und wird totsicher für Probleme rund um den Globus führen.

So müßte es jetzt für PHP 7.3 + PHP 8 geändert werden, damit es später noch funktioniert:

$this->currentDate->modify(‚+‘ . ($this->interval-1) . ‚ weeks‘);

Jetzt ist klar, daß hier „-“ Vorrang vor „.“ hat. Problem gelöst.

Wenn PHP nicht mehr ausgeführt wird

Wenn man einen Linux Webserver betreibt, der normalerweise UTF-8 benutzt, und ein Windowsfan speichert in seinem Lieblingstexteditor die PHP Datei ab, dann kann das voll in die Hose gehen.

Windows UTF-16LE in PHP Skripten

So geschehen bei einem Projekt das ich für Freunde betreue. Eine klitzekleine Anpassung an einem Textblock führte dazu, daß der Windowstexteditor der Wahl, statt dem vorgefundenen UTF-8, den Text als Windows Hausformat UTF-16LE abspeicherte.

Merken tut man das daran, daß man ums verrecken alles richtig im Webserver eingestellt hat, aber das PHP als HTML ausgegeben wird. Da wird sogar der PHP Interpreter korrekt aufgerufen, aber nicht mal der kann das Script korrekt als PHP erkennen und gibt es dann einfach als Text aus. Weil es keinen PHP Fehler gibt, ohne PHP auch kein Wunder, gibt es auch keine Fehlermeldung im Apachelogfile dazu.

Ihr könnt die mit vi , Gedit, Fokuswriter oder einem beliebigen anderen Editor aufmachen, keiner von denen wird Euch sagen, daß der Zeichensatz UTF-16LE ist und es klammheimlich genauso wieder abspeichern. Das PHP Script funktioniert dann einfach trotzdem nicht.

Des Rätsels Lösung

Wenn Ihr also mal vor einem Rätsel steht, wieso alle anderen PHP Scripte laufen, nur das eine nicht, könnte Euch das helfen:
1. mit „file filename.php“ den Typ bestimmen:

So müßte es aussehen:

camel.php: PHP script, UTF-8 Unicode (with BOM) text, with CRLF line terminators

So könnte es aussehen:

camel.php: Little-endian UTF-16 Unicode text, with CRLF, CR line terminators

2. So behebt Ihr es:

iconv -c -f UTF16LE -t UTF-8 < camel.php >camel2.php

Danach funktioniert das Script wieder und Ihr könnt dem Schuldigen die Ohren langziehen gehen 😉

Apache httpd 2.4.41 mit defektem PIPE support

Die Apache Webserver Version 2.4.41 hat einen defekten PIPE Support, was die Ausführung von CGI Scripten wie PHP Prozessen behindert. Abhilfe schafft nur ein Downgrade auf die vorherige Version.

Voraussetzungen für den Fehler

Als Voraussetzungen für den Fehler braucht man lange Ausgaben und die Ausführung von PHP als CGI, aber das ist natürlich nicht auf PHP begrenzt, es darf auf ein eigenes C Exe oder Perl sein. Bei uns war es ein PDF erzeugendes Script.

  1. httpd 2.4.41
  2. Das Script wird per CGI ausgeführt
  3. Das Script erzeugt lange Ausgaben von ~500kb

Die Symptome

Die Symptome an denen Ihr erkennen könnt, daß Ihr betroffen seid:

  1. Der Aufruf eines PHP Scripts per Browser timed aus.
  2. es stappeln sich die PHP Prozesse auf dem Server
  3. ein „strace -f -p ..hier_php_pid_angeben..“  zeigt nur eine Zeile an:
    … write( ………………….. ) = xXxXxx   , wobei die Xxx eine 6-x stellige Anzahl haben wird
  4. kurze Ausgaben, wie bei WordPresswebseiten üblich, werden normal abgearbeitet

Die Lösung

Für Fedora 29 lautet die Lösung einfach Downgraden:

dnf -y downgrade https://kojipkgs.fedoraproject.org//packages/httpd/2.4.39/3.fc29/x86_64/httpd-2.4.39-3.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/httpd/2.4.39/3.fc29/x86_64/mod_ssl-2.4.39-3.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/httpd/2.4.39/3.fc29/x86_64/httpd-tools-2.4.39-3.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/httpd/2.4.39/3.fc29/noarch/httpd-filesystem-2.4.39-3.fc29.noarch.rpm

systemctl restart httpd

Das war es dann auch schon. GGf. müßt Ihr noch liegen gebliebene Prozesse killen, aber die sollten beim httpd restart eigentlich von alleine terminieren, weil die PIPE endlich beendet wird.

Bugreport an Fedora ist raus, hat aber unverständlicherweise noch keine Reaktion hervorgerufen.