Was ändert sich mit PHP 5.3?

Seite 2: Späte Bindung

Inhaltsverzeichnis

Ein wesentliches Merkmal der objektorientierten Programmierung ist die dynamische beziehungsweise späte Bindung: Erst zur Laufzeit lässt sich entscheiden, welche Methode bei einem Methodenaufruf (besser: bei einer Nachricht an ein Objekt) tatsächlich auszuführen ist.

In den Vorgängerversionen war die dynamische Bindung nur für Instanzmethoden implementiert, die Bindung für Klassenmethoden also statisch. Dies kann man anhand des nachstehenden Beispiels nachvollziehen:

    <?php
class Base
{
public static function a()
{
print __METHOD__ . "\n";
self::b();
}

public static function b()
{
print __METHOD__ . "\n";
}
}

class Child extends Base
{
public static function b()
{
print __METHOD__ . "\n";
}
}

Child::a();
?>

Base::a
Base::b

Da man self anders als $this bereits in der Kompilierphase ausgewertet und durch den Namen der Klasse ersetzt hat, hat das Überschreiben der Methode in der Kindklasse nicht den gewünschten Effekt. PHP 5.3 bietet nun die Verwendung von static an, um zu notieren, dass für den Aufruf einer Klassenmethode die dynamische Bindung zu verwenden ist:

    <?php
class Base
{
public static function a()
{
print __METHOD__ . "\n";
static::b();
}

public static function b()
{
print __METHOD__ . "\n";
}
}

class Child extends Base
{
public static function b()
{
print __METHOD__ . "\n";
}
}

Child::a();
?>

Base::a
Child::b

Der Programmierer hat es in der Hand, ob er für den Aufruf einer Klassenmethode die dynamische oder statische Bindung verwenden soll.

Vor PHP 5.3 konnte man nur über eine Krücke, nämlich mit create_function(), eine anonyme Funktion (Lambda Function) deklarieren. In PHP 5.3 ist es möglich, eine anonyme Funktion direkt im Quelltext zu notieren:

    <?php
$list = array(22, 4, 19, 78);

usort(
$list,
function ($a, $b) {
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
}
);

print_r($list);
?>

Array
(
[0] => 4
[1] => 19
[2] => 22
[3] => 78
)

Diese Notation ist nicht nur deutlich lesbarer, dank ihr ist der PHP-Interpreter nun in der Lage, bereits in der Kompilierphase etwaige Syntaxfehler im Rumpf der anonymen Funktion zu entdecken. Ferner profitieren anonyme Funktionen von einem Bytecode-Cache.

Ebenso wie die Lambda-Funktionen haben Closures ihren Ursprung in der funktionalen Programmierung. Bei ihnen handelt es sich um anonyme Funktionen, die um eine Bindung an externe, sogenannte lexikalische Variablen erweitert sind. Anders als in anderen Programmiersprachen, die Unterstützung für Closures bieten, sind in PHP die lexikalischen Variablen explizit über das Schlüsselwort use zu deklarieren:

    <?php
$string = 'Hello World!';
$closure = function() use ($string) { print $string; };

$closure();
?>

Hello World!

In dem voranstehenden Beispiel bindet man die Variable $string lexikalisch an den Sichtbarkeitsbereich der anonymen Funktion: Wann immer die anonyme Funktion auszuführen ist, gibt sie den Inhalt der Variable $string aus, den diese zum Zeitpunkt der Deklaration der anonymen Funktion hatte.

Im Normalfall erfolgt die lexikalische Bindung derart, dass Änderungen der Variablen keine Auswirkung auf die lexikalische Variable der anonymen Funktion haben. Da das nicht immer gewünscht ist, lässt sich über den &-Operator die lexikalische Bindung per Referenz wählen:

    <?php
$x = 1;
$closure = function() use (&$x) { print $x . "\n"; };
$closure();
$x = 2;
$closure();
?>

1
2

Über anonyme Funktionen und Closures hinaus führt PHP 5.3 das Konzept der Funktionsobjekte ein. Ein Objekt einer Klasse, die eine __invoke()-Methode implementiert, ist aufzurufen, als wäre es eine Funktion:

    <?php
class Example {
public function __invoke() {
print __METHOD__ . "\n";
}
}

$object = new Example;
$object();
?>

Example::__invoke

Auf diese Weise lassen sich beispielsweise Callbacks mit Zustand implementieren.