Skip to content

Codeanalyse unter PHP 7 mit Phan

Nachdem ich auf meinem Entwicklungsrechner seit kurzem PHP 7 installiert habe, wollte ich einige (meiner) Projekte mit Hilfe des Code-Analyzers Phan, von dem vor dem Release der neuen PHP-Version schon einiges zu lesen war, auf Fehler untersuchen. Phan muss zwar unter PHP 7 ausgeführt werden, kann aber auch ältere Projekte, die beispielsweise für PHP 5.x entwickelt wurden, inspizieren.

Installation

Bevor es an die Installation von Phan geht, sind einige Voraussetzungen zu erfüllen: Insbesondere wird natürlich PHP 7 benötigt, zusätzlich aber auch die Extension php-ast, die den abstract syntax tree aufzeigen kann. Der Abstrakte Syntaxbaum (AST) ist neu in PHP 7 und stellt sozusagen einen Zwischenschritt dar, bei dem der PHP-Code in besser optimierbare Ausdrücke umgewandelt wird. Wie das bei HHVM im Detail aussieht, hat Sara Golemon ausführlich beschrieben. Zum Einlesen in die PHP-Implementierung ist der zugehörige RFC ein guter Einstiegspunkt.

In PHP 7 werden Skripte in einen Syntax-Baum zerlegt, der aus ast/Node-Objekten besteht, also z. B. einer Statement-Liste (AST_STMT_LIST), einer Variablen-Zuweisung (AST_ASSIGN) usw. Für ein ganz simples Skript kann der AST dann so aussehen:

Das Skript nutzt die ast_dump()-Funktion aus dem util.php-Utitlity, um die Ausgabe besser lesbar zu machen. Wichtig ist auch, dass der Methode ast\parse_code() der zweite Parameter ($version) mitgegeben wird; Version 30 ist zwar noch als unstable deklariert, hat aber bei mir bisher funktioniert.

AST-Erweiterung installieren

Die Extension php-ast ist als experimentell gekennzeichnet (also keinesfalls produktiv einsetzen!) und wird folgendermaßen kompiliert:

$ git clone https://github.com/nikic/php-ast.git
$ cd php-ast
$ phpize
$ ./configure
$ make
$ sudo make install

Danach sollte die Extension noch der php.ini hinzugefügt werden, z.B. mittels einer eigenen Konfigurationsdatei in conf.d/.

Phan installieren

Die Installation von Phan selbst ist einfach:

$ git clone https://github.com/etsy/phan.git
$ cd phan
$ composer install

Anwendung

Zum Ausprobieren eignen sich kleinere Projekte gut, ansonsten kann es passieren, dass einen die Menge der gefundenen Fehler erschlägt ;-)

Beispielhaft sei die Ausgabe von Phan hier an der Fotoverwaltung Lychee demonstriert, deren PHP-Code ich für verbesserungswürdig *hust* halte (ansonsten gefällt mir das Tool eigentlich gut). Weil die Dateien dieser Applikation in einer bestimmten Reihenfolge geladen werden müssen, habe ich sie in der Datei .filelist.txt aufgelistet, die von Phan eingelesen wird.

$ phan -f .filelist.txt
modules/Database.php:12 ParamError required arg follows optional
modules/Album.php:96 VarError Variable $return is not defined
modules/Photo.php:640 TypeError arg#2(timestamp) is string but \date() takes int
modules/Photo.php:128 TypeError Access to undeclared static property validExtensions on \photo
modules/Photo.php:725 VarError Variable $return is not defined
modules/Photo.php:768 TypeError arg#3(sub_arrays) is int but \exif_read_data() takes bool
modules/Photo.php:774 TypeError Suspicious array access to bool
modules/Photo.php:512 UndefError reference to undeclared class \imagick
modules/Photo.php:514 UndefError reference to undeclared class \imagickpixel
modules/Photo.php:444 UndefError reference to undeclared class \imagickexception
modules/Import.php:72 TypeError Access to undeclared static property validExtensions on \photo
modules/Import.php:80 TypeError Access to undeclared static property validTypes on \photo
modules/Import.php:136 VarError Variable $contains is not defined
modules/Photo.php:668 TypeError arg#2(timestamp) is string but \date() takes int
modules/Session.php:41 VarError Variable $return is not defined
api.php:40 VarError Variable $dbHost is not defined
api.php:40 VarError Variable $dbUser is not defined
api.php:40 VarError Variable $dbPassword is not defined
api.php:40 VarError Variable $dbName is not defined

Wir haben es hier mit einer ganzen Reihe von Fehlermeldungen zu tun, auch wenn dies nur ein Auszug aus dem Analyse-Ergebnis ist. Einige davon möchte ich näher erläutern:

  • "ParamError required arg follows optional" betrifft die Methodensignatur, genauer die Parameter: connect($host = 'localhost', $user, $password, $name = 'lychee') (siehe hier). Der erste Parameter $host ist optional, aber erforderliche Parameter sollten als erste aufgeführt werden.
  • "TypeError arg#2(timestamp) is string but \date() takes int": Im Handbuch steht, dass string date ( string $format [, int $timestamp = time() ] ) als zweiten Parameter einen Integer-Wert erwartet; hier wird aber ein String übergeben.
  • "VarError Variable $return is not defined" bedeutet, dass eine Variable benutzt wird, die vorher nicht initialisiert wurde. In diesem Fall wird $return als Array verwendet - $return['type'] = $info['mime']; -, ohne vorher definiert worden zu sein (siehe hier).

Dies sind nur drei Beispiele dafür, wie im Code unsauber gearbeitet wurde. Keiner dieser Fälle führt zu einem kritischen Fehler oder zum Abbruch des Skripts. Alle drei machen den Code aber konfuser und schlechter wartbar und verlangen dem Interpreter mehr ab als nötig. Ich denke, gerade bei größeren Projekten sollte auf entsprechend sauberen Code geachtet werden; hier kann Phan als (zusätzliches) Tool für die QA im Continuous Integration-Prozess durchaus sinnvoll sein.

Bonus: php7cc

Stefan hat mich auf php7cc aufmerksam gemacht. Dieses Tool untersucht PHP-Projekte auf ihre Kompatibilität mit der neuen 7er Version. Es versucht also, Befehle zu finden, deren Verhalten sich in PHP 7 geändert hat oder die zu fatal errors führen. Eine Liste solcher Befehle findet sich hier.

Der Einfachheit halber habe ich mir php7cc als PHAR-Paket heruntergeladen und ausgeführt:

$ php php7cc.phar ./php/</p>

<blockquote>
  <p>Line 30: Function argument(s) returned by "func_get_args" might have been modified
      func_get_args();
  [snip]

Dieser Hinweis fand sich in vielen Dateien des Projekts. Und tatsächlich hat sich hier etwas geändert:

The func_get_arg() and func_get_args() functions will no longer return the original value that was passed to a parameter and will instead provide the current value (which might have been modified). For example

function foo($x) {
    $x++;
    var_dump(func_get_arg(0));
}
foo(1);

will now print "2" instead of "1".

Mit Hilfe von php7cc lässt sich also schon frühzeitig prüfen, ob ein Skript unter PHP 7 ohne Probleme laufen sollte, oder ob Fehler auftreten können. Eine Garantie, dass alles zu 100% funktioniert, ist das zwar nicht. Aber mit diesen Hinweisen lassen sich Programme in den meisten Fällen so anpassen, dass sie sowohl unter PHP 5.x als auch PHP 7 laufen.

Aufgrund der (Geschwindigkeits-)Vorteile von PHP 7 dürften viele über den Umstieg auf diese Version nachdenken. Selbst kleinere Webhoster bieten einen Wechsel bereits heute schon an. Allerdings gibt es beim Upgrade einiges zu beachten. Die vorgestellten Tools helfen dabei, problematischen Code zu finden und zu bereinigen. Ob sie nun manuell ausgeführt oder in die Build-Toolchain eingebaut werden, hängt vom jeweiligen Projekt ab. Interessant sind die Einsichten aber allemal.

Trackbacks

Netz - Rettung - Recht am : Wellenreiten 12/2015

Vorschau anzeigen
Wer als “Websurfer” metaphorisch auf den Wellen des Netzes reitet, findet dabei zwar keine paradiesischen Inseln, manchmal aber immerhin ganz interessante Lektüre. Im Dezember 2015 kann ich u.a. folgende Fundstücke empfehlen und der werten Leserschaft an

Kommentare

Ansicht der Kommentare: Linear | Verschachtelt

Noch keine Kommentare

Kommentar schreiben

Markdown-Formatierung erlaubt
Wenn Du Deinen Twitter Namen eingibst wird Deine Timeline in Deinem Kommentar verlinkt.
Bewirb einen Deiner letzten Artikel
Dieses Blog erlaubt Dir mit Deinem Kommentar einen Deiner letzten Artikel zu bewerben. Bitte gib Deine Blog URL als Homepage ein, dann wird eine Auswahl erscheinen, in der Du einen Artikel auswählen kannst. (Javascript erforderlich)
Standard-Text Smilies wie :-) und ;-) werden zu Bildern konvertiert.
Die angegebene E-Mail-Adresse wird nicht dargestellt, sondern nur für eventuelle Benachrichtigungen verwendet.

Um maschinelle und automatische Übertragung von Spamkommentaren zu verhindern, bitte die Zeichenfolge im dargestellten Bild in der Eingabemaske eintragen. Nur wenn die Zeichenfolge richtig eingegeben wurde, kann der Kommentar angenommen werden. Bitte beachten Sie, dass Ihr Browser Cookies unterstützen muss, um dieses Verfahren anzuwenden.
CAPTCHA

Formular-Optionen

Kommentare werden erst nach redaktioneller Prüfung freigeschaltet!