Mit PHP 7 beschleunigt der User seine Anwendungen bis auf doppelte Geschwindigkeit zur Vorgängerversion. Der neue Schwung reißt aber auch einige Entwickler mit ins kalte Wasser – sie müssen ihre PHP-5-Skripte anpassen. Der Artikel beschreibt die wichtigsten Änderungen.
Zu Redaktionsschluss stand PHP 7 [1] kurz vor der Fertigstellung und sollte beim Erscheinen dieses Heftes bereits in der finalen Version zugänglich sein. Neben der konsistenten 64-Bit-Unterstützung bietet die neue Major-Release der Skriptsprache unter anderem Aufräumarbeiten bei einer Vielzahl unschöner Dinge. Die Installation des RC3 beschreibt der Kasten “PHP 7 auf die Platte”.
PHP 7 auf die Platte
Eine Vorabversion von PHP 7 erhält der Anwender unter Debian 8 durch Eingabe der Befehle aus Listing 1. Mit ihnen lädt er die Vorabversion aus dem Internet und installiert sie – unterstützt von den Kommandozeilenschaltern »–prefix« und »–with-config-file-path« – in sein Homeverzeichnis.
Anschließend ergänzt er den Ausdruck »export PATH=$PATH:$HOME/php7-RC03/usr/bin« am Ende der Datei »~/.bashrc« und legt zudem einen symbolischen Link auf das PHP-Binary (»cd ~/php7-RC03/usr/bin/ && ln -s php php7« ), um PHP 7 nach einem Neustart der Shell über die Eingabe von »php7« aufzurufen (Abbildung 1).
Listing 1
PHP 7 unter Debian 8
01 apt-get install autoconf libxml2-dev 02 wget -O- https://downloads.php.net/~ab/php-7.0.0RC3.tar.gz 03 tar xzvf php-7.0.0RC3.tar 04 cd php-7.0.0RC3 05 ./buildconf --force 06 ./configure --prefix=$HOME/php7-RC03/usr --with-config-file-path=$HOME/php7-RC03/usr/etc 07 make 08 make install
Vergangenheitsbewältigung
Die Ausdrucksstärke ihrer sprachlichen Mittel steigern die PHP-7-Entwickler, indem sie historisch gewachsene Unschönheiten bereinigen – auf Kosten mangelnder Rückwärtskompatibilität. Schuld an den Unannehmlichkeiten sind aber nicht unbedingt die kleineren Änderungen. So erlaubt PHP 7 zwar nur noch eine Default-Klausel pro Switch-Anweisung und verbietet gleichnamige Funktionsparameter wie in »function foo($value, $ignored, $ignored)« , doch waren solche Ausdrücke für PHP-Anwender ohnehin schon immer tabu. Ebenso empfahlen die PHP-Macher ihren Nutzern bereits vor dem Erscheinen von PHP 7, auf die Schlüsselworte »bool« , »int« , »float« , »string« , »null« , »true« und »false« als Namen für Klassen, Traits und Interfaces zu verzichten.
Gegen ASP-Tags wie etwa »<% […] %>« , »<= […] =>« und »<script language=”php”> […] </script>« , mit denen Entwickler üblicherweise PHP-Code eingrenzen, gab es hingegen ursprünglich keine Vorbehalte. Dennoch muss sich auf Arbeit einstellen, wer auf sie gesetzt hat, und diese Tags nach dem Releasewechsel adäquat mit »<?php […] >« austauschen.
Daneben wirft PHP 7 nun zum Beispiel Exceptions und löst zugleich das Fehlerniveau »E_STRICT« komplett auf. Indem es aber eine Vielzahl von Fehlermeldungen artikuliert, reagieren PHP-5-Anwendungen unter PHP 7 womöglich vollkommen anders als erwartet. Das gilt insbesondere für Anwendungen, die eigene Fehler-Handler einsetzen. Entwickler müssen in solchen Fällen notgedrungen den Quellcode durchforsten.
Klärungsprozess
Auch wer indirekte Variablen verwendet, muss seinen Code wohl in jedem Fall überarbeiten, denn den Ausdruck »$$foo[‘ref’]« wertet PHP 5 noch sinngemäß wie den Pseudoausdruck »$($foo[‘ref’])« aus. PHP 7 interpretiert ihn hingegen ungefähr als »$($foo)[‘ref’]« , denn in der neuen Major-Release wirkt der Dereferenzierungsoperator »$« strikt von links nach rechts (Abbildung 2). Dadurch gibt Zeile 5 von Listing 2 die Zeichenkette »foo« aus.
Listing 2
Indirekte Variablen expandieren
01 <?php
02 $foo = 'bar';
03 $bar = ['ref' => 'foo'];
04
05 echo $$foo['ref'];
06 echo ${$bar['ref']};
07 ?>

Listing 2 schraubt PHP 7 das Wort »foobar« zusammen.” width=”260″ height=”129″ /> Abbildung 2: In Listing 2 schraubt PHP 7 das Wort »foobar« zusammen.
Wer geschweifte Klammern verwendet, kann die Expansion steuern und durch kluge Wahl das Verhalten von PHP 5 wiederherstellen, wie es die letzte Zeile des Listings tut. Zeile 6 liefert die Zeichenkette »bar« zurück. Wer die Listings in PHP 7 ausführen möchte, muss den Code jeweils mit den Begrenzern »<?php […] ?>« umgeben. Andernfalls erkennt PHP ihn nicht und zeigt lediglich den Quellcode an.
Auch die Foreach-Schleife behandelt Variablen unter PHP 7 etwas anders. Iteriert die Skriptsprache über ein Feld, wirken sich Änderungen für das Feld, die von Code innerhalb des Schleifenkörpers stammen, nicht unbedingt aus (Listing 3). Denn PHP 7 iteriert in der Schleife über eine Kopie des Feldes »$arr« aus Zeile 2. Löscht das Skript also das zweite Feldelement mittels »unset($arr[1])« (Zeile 6) hat das keinen Effekt. Das Skript gibt die Werte »1« , »2« und »3« in der Shell aus (Abbildung 3, links).
Listing 3
Iterationen bleiben unbeschädigt
01 <?php
02 $arr = [1,2,3];
03
04 foreach ($arr as $val) {
05 echo $val;
06 unset($arr[1]);
07 }
08 ?>
Wie schon zuvor lässt sich die Rückwärtskompatibilität zu PHP 5 restaurieren. Soll der Code direkt über das Feld aus Zeile 2 iterieren, fügt der Entwickler im Schleifenkopf den Referenzoperator »&« vor der Laufvariablen »var« ein:
foreach ($arr as &$var)
Im Ergebnis zeigt PHP 7 die Werte »1« und »3« an (Abbildung 3, rechts). Anders als PHP 5 erhöht die neue Version den Feldzeiger von einer Foreach-Schleife nicht, weswegen der Aufruf der Funktion »current()« stets den gleichen Wert ausgibt.
Mit dem Indizieren von Feldern haben die Entwickler eine prominente Nebenwirkung beseitigt. Die Ordnung eines Feldes bleibt unter PHP 7 auch dann literal, wenn das Skript ein Feld über eine Referenz mit dem Wert eines folgenden Schlüssel-Wert-Paares verknüpft (Listing 4, Zeile 3). Die letzte Zeile würde »a« und »b« in der Ausgabe »array(2) {[“a”]=>&int(0)[“b”]=>&int(0)}« unter PHP 5 vertauschen.
Listing 4
Die Ordnung ist rein literal
01 <?php 02 $arr = []; 03 $arr['a'] = & $arr['b']; 04 $arr['b'] = 0; 05 06 var_dump($arr); 07 ?>
Kultivierung
Längst überfällig waren auch die Reparaturarbeiten am »list« -Konstrukt. Der Ausdruck »list($a[], $a[], $a[]) = array(1,2,3);« speichert die Zahlen aus dem Feld rechts neben dem Gleichheitszeichen nun in der korrekten, aufsteigenden Reihenfolge: »1« , »2« , »3« unter »$a« ab. Zusätzlich zerlegt »list« alle Objekte, deren Vorlageklassen das Array-Access-Interface implementieren, etwa:
list($a, $b) = (object) new ArrayObject([0, 1])
Zeichenketten aus Variablen extrahiert PHP 7 hingegen nicht mehr, leere »list« -Ausdrücke wie »list() = $a;« geben zusätzlich einen Fehler aus.
Auch Datentypen behandelt PHP 7 anders. Illegale Ausdrücke für oktale Zahlen wie »0128« lösen nun einen Parse Error aus, Bit-Shift-Operationen mit negativen Zahlen wie »1 >> -1 « eine Exception vom Typ »ArithmeticError« . Bit-Shift-Operationen, deren Ergebnis die Breite eines Integer überschreitet, wertet PHP 7 stets nach 0 aus. Zuvor war das Ergebnis solcher Operation fatalerweise abhängig von der eingesetzten Prozessorarchitektur. Auch auf das Teilen durch 0 reagiert PHP 7 anders. Hexadezimale Zahlen innerhalb von Zeichenketten interpretiert es nicht mehr und der Ausdruck »’0xf’ == 0xf« ist immer falsch.
Wer sich nun noch immer wundert, warum seine PHP-Skripte noch nicht funktionieren, sollte einen Blick auf die Liste der geänderten Funktionen [2] werfen. Der Vollständigkeit halber sei angemerkt, dass doppelte Klammern um Rückgabewerte von Funktionen einfach nur noch formal redundant sind und »global« nur einfache Variablen akzeptiert.
Optimierungsgebaren
Wer seine Skripte ohnehin durchgeht, kann auch gleich an passender Stelle einige der neuen Features von PHP 7 einbauen. Seine Änderungen überprüft der Anwender mit der »assert()« -Funktion direkt im Code. Setzt er »ini_set(‘zend.assertion’, -1)« , übergeht PHP 7 die Funktionsaufrufe von »assert()« im Produktionsmodus kostenneutral. Der neue Coalesce-Operator »??« gestaltet den Programmcode übersichtlicher und erspart Tipparbeit (Zeile 3, Listing 5).
Listing 5
Neuer Coalesce-Operator
01 <?php 02 $result = isset($var)?$var:isset($bar)?$bar:1; 03 $result = $var ?? $bar ?? 1; 04 ?>
Ähnlich elegant entfaltet sich im Programmcode der neue kombinierte Vergleichsoperator »<=>« , auch Spaceship-Operator genannt. Er arbeitet binär und vergleicht zwei Werte bezüglich der Relationen »>« , »<« und »=« in einem Atemzug. Für den Fall der Gleichheit wertet PHP 7 den Ausdruck »1 <=> 1« zu »0« , für kleiner »0 <=> 1« zu »-1« und für größer »1 <=> 0« zu »1« aus. Der Operator macht sich die lexikalische Ordnung in der Menge aller Zeichen zunutze, um Zeichenketten vergleichbar zu gestalten. Den Ausdruck »”foo” <=> “bar”« wertet er als »1« .
Daneben lassen sich »use« -Anweisungen platzsparend in einer Zeile zusammenfassen, etwa so:
use foo\bar{Class Foo, Class Bar as FooBar}
Im Übrigen bietet PHP 7 die Möglichkeit, mittels »define« Felder zu Konstanten zu erheben und Unicode-Zeichen mit Hilfe einer Escape-Syntax wie in »”\u{af}”« innerhalb von Zeichenketten zu repräsentieren. Die Skriptsprache kodiert einzelne Zeichen über ihren hexadezimalen Code im Unicode-Standard.
Kulturschock
Die wohl augenfälligste Neuerung in PHP 7 bringt Typdeklarationen in Funktionen zum Einsatz. Auf Basis des zugleich rückwärtskompatiblen Mechanismus kann der PHP-Anwender die Datentypen sowohl der Aufrufparameter als auch des Rückgabewerts einfordern, wie es Listing 6 demonstriert. Die Anweisung »declare(strict_types=1)« in Zeile 2 aktiviert die Typdeklaration. Der »…« -Operator vor dem Parameter »$ints« bugsiert in Zeile 4 des Listings alle Aufrufparameter in das Feld »$ints« . Die Typdeklaration gilt dabei für jeden einzelnen Aufrufparameter.
Listing 6
Striktes Einhalten von Datentypen
01 <?php
02 declare(strict_types=1);
03
04 function sum(int ...$ints): string {
05 return array_sum($ints);
06 }
07
08 echo sum(1, 2, "3");
09 echo sum(1, 2, 3);
10 ?>
Fehlte Zeile 2, würde Zeile 4 die Aufrufe der Funktion »sum()« in den Zeilen 8 und 9 mit Hilfe der Funktion »array_sum()« wie gewohnt jeweils zum Rückgabewert »6« addieren. Dank Zeile 2 scheitern beide Aufrufe. Stattdessen generiert der Code ab Zeile 4 für die Zeilen 8 und 9 eine Exception vom Typ »TypeError« . Auf Code, den PHP über »require« oder »include« einbindet, wirkt »strict_types« wechselseitig.
Wegwerfgesellschaft
Mit Hilfe anonymer Klassen generiert PHP 7 Objekte zum einmaligen Gebrauch (Listing 7). Offenbar sind die gemeinsamen Jahre mit Javascript nicht spurlos vorübergegangen. Zeile 6 erzeugt ein leeres Objekt aus der anonymen Klasse »{}« . Analog zu anonymen Funktionen brauchen auch anonyme Klassen keinen Namen. Der Entwickler gibt sie in einer Zuweisung direkt nach dem »new« -Operator an.
Listing 7
Anonyme Klassen
01 <?php
02 interface Calculator {
03 public function result(float $value);
04 };
05
06 $Prototype = new class {};
07
08 $Prototype->calculator = new class implements Calculator {
09 public function result(float $value) {
10 return 2*$value;
11 }
12 };
13
14 echo $Prototype->calculator->result(2);
15 ?>
In Zeile 8 lässt sich beobachten, wie PHP 7 das leere Objekt »$Prototype« zur Laufzeit um die Eigenschaft »calculator« erweitert. Sie speichert die Instanz eines Objekts, welches das Skript zuvor mit der anonymen Klasse instanziert hat (Zeile 8). Diese Klasse implementiert das Interface ab Zeile 2, Zeile 9 definiert die geforderte Funktion »result()« . Die Funktionstüchtigkeit des Konstrukts beweist der Ausdruck in der letzten Zeile des Listings, der den Wert 4 ausgibt.
Delegiertenwesen
PHP 7 setzt Generatoren universeller ein. An dieser Stelle sei erwähnt, dass der »yield« -Operator künftig stets rechtsassoziativ funktioniert. Listing 8 demonstriert, dass Generatoren nun nicht mehr nur in Schleifen, sondern auch in Generatoren selbst einsetzbar sind – Generator-Delegation im Fachjargon.
Listing 8
Verschachtelte Generatoren
01 <?php
02 $fibo1 = (function() {
03 yield 1;
04 yield 2;
05 yield from fibo2();
06 return 8;
07 })();
08
09 function fibo2() {
10 yield 3;
11 yield 5;
12 }
13
14 foreach($fibo1 as $val) {
15 echo $val;
16 }
17
18 echo $fibo1->getReturn();
19 ?>
In den ersten sechs Zeilen erzeugt das Listing zunächst ein Generatorobjekt, indem es eine anonyme Funktion aufruft und sie in der Variablen »$fibo1« ablegt. Wie gehabt erzeugen »yield« -Anweisungen die Rückgabewerte des Generators. Zeile 5 bezieht den Rückgabewert von einem zweiten Generator (Zeile 9). Dessen Aufruf stellt der Entwickler das Schlüsselwort »from« voran.
Der Code ab Zeile 13 iteriert wie gewohnt mittels einer Foreach-Schleife über das anfangs erwähnte Generatorobjekt. Zeile 14 gibt die ersten Fibonacci-Zahlen so lange über die Shell aus, bis sie Rückgabewerte von sämtlichen »yield« -Anweisungen empfangen hat. Mit der »return« -Anweisung in Zeile 6 erzeugt PHP 7 einen weiteren, finalen Wert. Den liest die Methode »getReturn()« in der letzten Zeile von Listing 8 aus dem Generatorobjekt aus.
Fazit
PHP 7 bereinigt viele Unschönheiten der populären Websprache um den Preis fehlender Rückwärtskompatibilität. Selbst einfachere PHP-Skripte könnten am neuen Unterbau anecken. Die Akzeptanz von PHP 7 dürfte aber hoch ausfallen, wenn sich die Performance gegenüber der Vorgängerversion 5.6 tatsächlich verdoppelt hat, wie es die Macher und einige Benchmarks [3] nahelegen. Auch die Typdeklarationen in Funktionen tun PHP-Programmen gut, da sie dem User mehr Sorgfalt abringen.
In Anbetracht der weiteren Änderungen, die auch Standard-Funktionen, -Klassen und -Interfaces betreffen, sei Entwicklern auf jeden Fall die eingehende Lektüre der Dokumentation zu PHP 7 [2] ans Herz gelegt. Trotz dieser Klippen dürfte die Major-Release viele Webentwickler in ihrer Überzeugung bestätigen, mit PHP das beste Mittel zur Server-seitigen Programmierung von Webanwendungen zur Hand zu haben.
Infos
- PHP 7: http://php.net
- Geänderte Funktionen in PHP 7: http://php.net/manual/de/migration70.php
- PHP-7-Benchmarks: https://www.reddit.com/r/PHP/comments/305ck6/real_world_php_70_benchmarks/








