Sicher ist sicher

Typsichere Programmierung in PHP
Kommentare

Im Gegensatz zu Java oder C++ ist PHP eine schwach typisierte Sprache. Ohne einen weiteren Glaubenskrieg in der Entwickler-Community herbeizuführen kann man sagen, dass dies sowohl Vor- als auch Nachteile haben kann. Dieser Artikel soll Ihnen Möglichkeiten und Strategien aufzeigen, wie Sie auch in PHP eine gewisse Typsicherheit erzielen können, ohne auf die angenehme Flexibilität der Sprache verzichten zu müssen.

Letztendlich spielen ja zwei Faktoren auf jeder Seite eine wichtige Rolle: 1. Ein stark typisiertes System zieht nicht zu leugnende Einschränkungen nach sich, was die Flexibilität und Einfachheit des Codes betrifft. 2. Ein schwach typisiertes System erfordert nicht zu leugnenden Mehraufwand beim Überprüfen von Variablen und ermöglicht so das allseits beliebte „sich selbst in den Fuß schießen“ wesentlich einfacher hinzu bekommen. Betrachten wir also in erster Linie den zweiten Punkt und schauen uns an, was hier machbar ist.

Der Sinn der Typisierung

Bekanntermaßen gibt es in PHP vier grundlegende Datentypen: Skalar, Array, Objekt und Ressource. Wobei sich Skalare und Objekte jeweils in verschieden Untertypen aufteilen. Für Skalare sind dies Boolean, Integer, Double (auch Float genannt) und String. Bei Objekten ist der Untertyp die jeweilige Objekt-Klasse. Obwohl PHP schwach typisiert ist und keine explizite Deklaration für eine verwendete Variable erforderlich ist, hat doch jede Variable zu jedem Zeitpunkt einen genau definierten Typ. Problematisch (oder auch praktisch) ist, dass sich der Typ jedoch je nach Verwendung dynamisch ändern kann. Eine Variable vom Typ Integer kann im nächsten Moment bereits ein Objekt enthalten und drei Zeilen später zu einem Array werden, ohne das der PHP-Compiler Beschwerde einlegt, siehe auch das folgende Listing: Listing 1
bar = 23; // Attribut $bar ist Integer 23

$foo = (array)$foo; // Array array("bar" => 23)
$foo["baz"] = 42; // Array array("bar" => 23, "baz" => 42)

$foo = (object)$foo; // Objekt mit $bar = 23; $baz = 42;

?>
Ebenfalls zwiespältig lässt sich der Umstand betrachten, dass PHP kaum über Möglichkeiten verfügt, die erwarteten Parameter-Typen einer Funktion/Methode festzulegen. Zwar sind seit PHP 5 für Objekte, und seit 5.1 auch für Arrays, sogenannte Type Hints verfügbar, spätestens aber bei skalaren Werten lässt der Interpreter freizügig jeden beliebigen Typ zu.

Möglichkeiten und Limitierungen

Seit Version 5.0 verfügt PHP also über einen Mechanismus namens Type Hints, mit dem es ermöglicht wird in Funktions- und Methoden-Signaturen den Typ erwarteter Objekte festzulegen. Der Code-Schnipsel in Listing 2 stellt die Möglichkeiten dar: Listing 2

Die Funktion foo() erwartet drei Parameter. Der erste sollte möglichst vom Typ Integer sein, allerdings lässt sich dies in PHP nicht festlegen. Anders ist es beim zweiten und dritten Parameter, denn hier wird explizit ein Objekt vom Typ TestClass bzw. ein Array erwartet. Der erste Beispielaufruf der Methode funktioniert problemlos, denn alle Parameter haben den korrekten Typ. Auch der zweite Aufruf ruft keine Probleme hervor, denn der erste Parameter ist an keinen Typ gebunden (Type Hints existieren in PHP für skalare Werte nicht). Die letzten beiden Aufrufe führen jedoch zu einem Fehler. Im ersten Fall ist der übergebene Wert „42“ keine Instanz der Klasse TestClass, im 2. Fall ist dieser Wert kein Array. Das gezeigte Verhalten funktioniert auch, wie man erwarten möchte, mit Interfaces und durch die Vererbungshierarchie von Objekten, wie das Beispiel in Listing 3 illustriert: Listing 3

Es werden drei Interfaces deklariert, wobei das Interface iExtended das Interface iBase erweitert. Daneben werden drei Klassen definiert. Zwei davon implementieren jeweils eines der Interfaces, die dritte klasse erweitert die Klasse cBase und implementiert das erweiterte Interface iExtended. Die Funktion foo() erwartet drei verschiedene Typen als Parameter: Jeweils eine Instanz, dieiBase, bzw. iExtended implementiert und ein Objekt der Klasse cExtended.

Der erste Aufruf der Funktion foo() verläuft erfolgreich. Die Klasse cBase() implementiert das geforderte Interface iBase, gleiches gilt für cExtended und iExtended. Mit cExtended wird ebenfalls der korrekte Typ übergeben. Auch der zweite Aufruf funktioniert ohne Probleme. Da cExtended das Interface iExtended implementiert und dieses iBase erweitert, beschwert sich der Compiler beim ersten Parameter nicht. Beim zweiten und dritten Parameter hat sich nichts gegenüber dem ersten Aufruf geändert.

Der dritte Aufruf allerdings resultiert in einem Fehler. Auch wenn die Bedingungen für Parameter Eins und Zwei erfüllt sind, erweitert cExtendedBaseExtended nicht von cExtended.

Der gezeigte Type Hint-Mechanismus ist eine praktische Erweiterung der Programmiersprache und erlaubt ein gewisses Maß an Typsicherheit zu nutzen, ohne es explizit zu erzwingen. Die Syntax der Type Hints erinnert stark an Java und C++ und ist damit für Programmierer sehr intuitiv. Allerdings sind Type Hints längst kein Allheilmittel, denn wie Sie gesehen haben funktionieren sie nicht für skalare Typen. Ein weiteres Manko ist, dass Type Hints nur dann funktionieren, wenn wirklich nur ein bestimmter Objekt-Typ oder eine seiner Erben erwartet wird. Kann eine Funktion mit zwei verschiedenen Objekt-Typen (nicht aber mit jedem beliebigen Typ) umgehen, steht man mit Type Hints wieder auf dem Schlauch. Und gerade dieser Punkt ist in der objektorientierten Programmierung mit PHP sehr gebräuchlich; die Möglichkeit Methoden zu überladen (also die gleiche Methode mit unterschiedlichen Signaturen zu deklarieren) fehlt.

Virtual Properties

In den letzten Abschnitten haben Sie gesehen, welche Möglichkeiten PHP von Hause aus zur typsicheren Programmierung bereits mitbringt und wo die Limitierung der Sprachfeatures hierfür liegen. Oftmals wünscht man sich aber ein wesentlich höheres Maß an Typsicherheit, besonders wenn es um die Attribute eines Objektes geht. In diesem Fall ist es wünschenswert zu jedem Zeitpunkt und an jeder Stelle einer Klasse sicherzustellen, dass ein Attribut nur mit bestimmten Typen und ggf. bestimmten Wertbereichen belegt ist. Natürlich ist es umständlich und fehleranfällig diese Sicherheit bei jedem Zugriff auf die Attribute erneut festzulegen. Aus diesem Grund soll in diesem Abschnitt ein Konzept vorgestellt werden, was ich gerne als Virtual Properties bezeichne und womit die beschriebene Funktionalität auf einfache, komfortable und generische Weise realisiert werden kann. Listing 4 zeigt eine entsprechende Beispielklasse: Listing 4
 "",
"score" => 0,
"round" => 1,
);
	
public function __construct( $name )
{
$this->name = $name;
}

public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case "name":
if ( !is_string( $propertyValue ) || strlen( $propertyValue 
9." );
}
break;
case "score":
if ( !is_int( $propertyValue ) || $propertyValue  0 and
divisible through 3." );
}
break;
case "round":
if ( !is_int( $propertyValue ) || $propertyValue  0." );
}
break;
default:
throw new RuntimeException( "$propertyName does not exist." );
}
$this->properties[$propertyName] = $propertyValue;
}

public function __get( $propertyName )
{
if ( isset( $this->$propertyName ) )
{
return $this->$properties[$propertyName];
}
throw new RuntimeException( "$propertyName does not exist." );
}

public function __isset( $propertyName )
{
return ( array_key_exists( $propertyName, $this->properties ) );
}

public function roleDice()
{
$this->score += mt_rand( 1, 6 ) * 3;
$this->round++;
}
}

?>
Instanzen der Klasse aus diesem Beispiel stellen ein rudimentäres Spieler-Objekt für ein einfaches Würfelspiel dar. Ein Spieler muss einen Namen haben und hat implizit eine Punktzahl (); außerdem wird gespeichert in welcher Rund er ist. Die erste Auffälligkeit gegenüber Implementierungen, wie sie weithin bekannt sein dürften, ist die Ablage der Objekt-Attribute in einem gemeinsamen Array , statt diese direkt zu deklarieren. Wozu dies sinnvoll ist, werden Sie schon wenige Zeilen später feststellen. Neben des Konstruktors, der einfach das Namens-Attribut des Objektes setzt, implementiert die Klasse die drei Overloading-Methoden __get(), __set() und __isset() sowie eine Methode, die den Spieler veranlasst einen Würfel zu werfen (roleDice()). Bereits der Konstruktor mutet eigenartig an, denn das Attribut auf das er zugreift existiert eigentlich gar nicht. Außerdem handelt dieser Artikel vom Thema „Typsicherheit“ und es findet keinerlei Überprüfung statt. Als PHP-Kenner wissen Sie allerdings, dass es beim Schreibzugriff auf nicht existierende Attribute in PHP zwei Möglichkeiten gibt, wie der Interpreter reagiert:
  1. Fall existierend, wird die Methode __set() aufgerufen.
  2. Sonst wird das Attribut on-the-fly erzeugt und der Wert gesetzt.
In diesem Beispiel tritt natürlich der erste Fall auf. Über die Methode __set() wird der gewünschte Wert dem Attribut zugewiesen. Und hier findet ebenfalls die Typprüfung statt. Innerhalb der Methode __set() findet sich ein langes switch-Statement, welches einen case-Block für jeden Attribut-Namen hat. Innerhalb des entsprechenden case-Blocks findet dann die Typprüfung des zu setzenden Wertes statt. Ist der zu setzende Wert nicht valide, wird eine Ausnahme ausgelöst. Dies passiert ebenfalls, wenn das gewünschte Attribut gar nicht existiert. Somit wird auch das Verhalten von PHP unterdrückt, nicht existierende Attribute beim Schreibzugriff on-the-fly zu erzeugen. Hierdurch entstehen oft Flüchtigkeitsfehler, die äußerst schwierig zu debuggen sind. Ist alles in Ordnung, wird das Ende des switch-Statements erreicht und der gewünschte Wert gesetzt. Nun sehen Sie auch, weshalb statt normaler geschützter Attribute in diesem Beispiel das -Array benutzt wird. Auf geschützte Attribute kann innerhalb einer Klasse immer noch direkt zugegriffen werden. Um aber den Typ und Wertebereich der Attribute verlässlich zu sichern, ist dies unpraktisch, denn bei einem Bug in einer der Methoden könnte bei direktem Zugriff immer noch ein falscher Wert gesetzt werden, was unerwünscht ist. Also werden auch interne Zugriffe auf die Attribute umgeleitet. Um die Sicherheit der Attributtypen und -wertebereiche muss sich also nur noch an einer zentralen Stelle explizit und sehr übersichtlich gekümmert werden. Durch die Verwendung des -Arrays, müssen noch zwei weitere Overloading-Methoden definiert werden, damit der lesende Zugriff und die Überprüfung ob ein Attribut existiert ebenfalls möglich ist. Die Methode __get() überprüft lediglich ob ein Attribut existiert und gibt gegebenenfalls dessen Wert zurück. Existiert es nicht, wird wiederum eine Ausnahme ausgelöst, was ein weiterer Vorteil ist, denn normalerweise würde PHP nur eine unschöne Notiz ausgeben und mit der Ausführung des Programms fortfahren. Dies ist jedoch in den seltensten Fällen erwünscht, denn auch beim lesenden Zugriff auf nicht existierende Attribute handelt es sich meist um einen Flüchtigkeitsfehler. Die Methode __isset() überprüft ob ein Attribut vorhanden ist. Hierbei wird nicht auf die PHP-interne Funktion isset() sondern auf array_key_exists() zugegriffen. Damit bügelt man auch ein manchmal unschönes Verhalten von PHP aus: isset() gibt nicht nur false zurück falls ein Attribut nicht existiert, sondern auch, wenn dessen wert der Typ null ist. Ein Attribut kann jedoch durchaus mit dem Typ null initialisiert werden und dennoch existieren, daher wird hier array_key_exists() benutzt, welches in diesem Fall true liefert. Die letzte Methode der Klasse DiceGamePlayer, roleDice(), führt echte Applikationslogik aus. Der virtuelle Würfel wird mit Hilfe der Funktion mt_rand() gewürfelt und die gewürfelte Augenzahl mal drei auf den aktuellen Punktestand aufaddiert. Außerdem wird die Rundenzahl des Spielers um Eins erhöht. Bei beiden Attributzugriffen muss man sich keine Gedanken mehr um die Inhalte der entsprechenden Attribute machen. Es ist sichergestellt, dass beide einen Integer-Wert größer Null enthalten. Auch über den Schreibzugriff muss nicht nachgedacht werden. Sollte man einen Fehler beim Programmieren gemacht haben und die neue Punktzahl zum Beispiel nicht durch drei teilbar sein, wird eine Ausnahme ausgelöst. Ein weiterer Vorteil der Virtual Properties kommt beim externe Zugriff auf Attribute zum Vorschein. Würde man hierzu normale Attribute als public deklarieren, wäre keine Typprüfung möglich. Der Standardweg, eine Typ- und Wertebereichsprüfung beim externen Schreibzugriff auf Attribute einzuführen, sind die aus Java bekannten Setter-Methoden (z.B. setScore()). Natürlich ist dieser Weg ebenfalls valide und durchaus auch für die Sicherung bei internen Zugriffen geeignet. Allerdings sind sie zum Ersten recht unschön anzusehen und es ist unkomfortabel damit zu programmieren. Zum Zweiten muss sich jeder Entwickler beim internen Zugriff an die entsprechende Konvention halten, was schnell vergessen wird, und letztendlich erfordert die Implementierung der Setter– undGetter-Methoden wesentlich mehr Code, wenn es um viele Attribute geht, und ist unübersichtlicher. Der direkte Attribut-Zugriff ist einfach intuitiver.

Read-only Properties

Ein weiterer Vorteil, den man mit Hilfe von Virtual Properties erreichen kann, ist die Möglichkeit Attribute zu schaffen, auf die von außen nur lesend zugegriffen werden kann. Allerdings verliert man hierdurch den Vorteil der Typsicherung bei internen Zugriffen. Trotzdem kann es oft praktisch sein, den Schreibzugriff von außen zu verbieten und nur das Auslesen von bestimmten Attributen zu erlauben. Um dies zu illustrieren wird die __set()-Methode aus der Würfelspieler-Klasse wie in Listing 5 dargestellt umgeschrieben: Listing 5
<?php
// ... Würfelspielerklasse
public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case "name":
if ( !is_string( $propertyValue ) || strlen( $propertyValue 
9." );
}
break;
case "score":
case "round":
throw new RuntimeException( "The property '$propertyName' is
read-only' );
default:
throw new RuntimeException( "$propertyName does not exist." );
}
$this->properties[$propertyName] = $propertyValue;
}

?>

Statt für die Attribute und die Typ- und Wertbereichssicherung vorzunehmen wird direkt eine Ausnahme geworfen. Nun muss zwar der interne Zugriff auf die Attribute direkt über das -Array erfolgen, der Zugriff von außen ist jedoch nur lesend möglich und die Punkt- und Rundenzahl des Spielers sind von außen nicht mehr manipulierbar. Dass bei Entwickeln von Methoden der Klasse nun die Prüfung der Werte beim Setzen manuell erfolgen muss ist zwar unschön, wird aber immerhin forciert: Versucht ein Entwickler nun direkt in das entsprechende Attribut zu schreiben, wird er durch die Ausnahme, dass der Zugriff nur lesend möglich ist, daran erinnert den direkten Zugriff über das -Array zu verwenden und somit implizit auch daran, dass er eine entsprechende Prüfung des zu setzenden Wertes vornehmen muss.

Arrays, Hashtables, Listen und mehr…

Man empfindet es meist als praktisch und komfortabel, dass alle gängigen einfachen Kompositionstypen in PHP als Arrays abgelegt werden. So können Arrays behandelt werden als seien Sie echte Arrays (also numerisch indiziert), Hashtables, Sequenzen, Listen oder Stacks. An manchen Stellen ist dies jedoch unpraktisch, nämlich dann, wenn man explizit einen dieser Kompositionstypen erwartet. Wie im vorhergehenden Abschnitt müsste nun bei jedem Zugriff auf die Datenstruktur explizit überprüft werden, dass die ausgeführte Operation mit den entsprechenden Parametern valide ist oder dass die Datenstruktur noch konsistent ist. In diesem Abschnitt soll anhand eines echten Arrays gezeigt werden, dass es auch komfortabler und generischer geht. Mit Hilfe der Interfaces ArrayAccess, Iterator und Countable aus der Standard PHP Library (SPL) wird ein echtes Array mit Hilfe eines Objektes emuliert. Ein echtes Array hat eine vorbestimmte Länge, einen Vorbestimmten Daten-Typen für seine Elemente und nur numerische Indizes, die zwischen 0 und der Länge –1 liegen müssen. Listing 6 zeigt die Basis der Klasse RealArray, welche annähernd ein echtes Array emulieren wird: Listing 6
<?php

class RealArray implements ArrayAccess, Iterator, Countable
{
protected $length;

protected $type;

protected $array = array();

public function __construct( $length, $type )
{
if ( !is_int( $length ) || $length  0." );
}
$this->length = $length;
$this->type = $type;
}

// ...
}

?>
Ein Objekt der Klasse RealArray besitzt drei Attribute, wobei im Attribut die eigentlichen Daten gehalten werden. Das Attribut behält die Maximallänge des Arrays und verwaltet den Datentyp der Werte. Ob man bei einer praktischen Implementierung wirklich eine Länge verwalten und forcieren möchte ist fraglich, aber für die möglichst nahe Emulation eines echten Arrays ist dies von Nöten. Der Konstruktor eines Arrays nimmt die Länge und den Element-Datentyp entgegen. Da ein Array numerisch indiziert ist und der kleinste Index die 0 ist, wird die Länge zunächst überprüft. Liegt sie nicht im zulässigen Rahmen wird eine OutOfBoundsException (der Name sollte Java-Kennern irgendwie bekannt erscheinen) geworfen. Diese Exception ist, wie die implementierten Interfaces, Teil des SPL-Paketes und wird der Praktikabilität wegen recycelt. In Listing 7 werden der Klasse RealArray die benötigten Methoden des ArrayAccess-Interfaces hinzugefügt, welches den Zugriff auf die Werte des Objektes über die gewohnte Array-Syntax []erlaubt: Listing 7
checkOffset( $offset );
return isset( $this->array[$offset] );
}

public function offsetGet( $offset )
{
return isset( $this[$offset] ) ? $this->array[$offset] : null;
}

public function offsetSet( $offset, $value )
{
$this->checkOffset( $offset );
$this->checkValue( $value );
$this->array[$offset] = $value;
}

public function offsetUnset( $offset )
{
$this->checkOffset( $offset );
if ( isset( $this->$offset ) )
{
unset( $this->array[$offset] );
}
}

// ...
?>
Alle Methoden verwenden eine weitere, geschützte Methode checkOffset(), welche überprüft, ob der übergebene Offset den Einschränkungen genügt. Die Methode offsetSet() überprüft zusätzlich mit Hilfe einer weiteren geschützten Methode checkValue() die Validität des übergebenen Wertes. Beide Methoden werfen mit entsprechenden Exceptions um sich, so dass nach dem Aufruf der Methoden die Werte gefahrlos benutzt werden können. Die Implementierung der Check-Methoden sehen Sie in Listing 8: Listing 8
<?php

class RealArray implements ArrayAccess, Iterator, Countable
{
// ...

protected function checkOffset( $offset )
{
if ( !is_int( $offset ) || $offset = $this->length )
{
throw new OutOfBoundsException( "Offset '$offset' is illegal, must be integer >= 0 and length}." );
}
}

protected function checkValue( $value )
{
switch ( $this->type )
{
case 'boolean':
case 'integer':
case 'double':
case 'string':
case 'array':
case 'resource':
if ( gettype( $value ) !== $this->type )
{
throw new UnexpectedValueException( "Illegal value type '" . gettype( $value ) . "', expecting {$this->type}." );
}
break;
default:
if ( is_object( $value ) === false || ( $value instanceof $this->type ) === false )
{
throw new UnexpectedValueException( "Illegal value type '" . get_class( $value ) . "', expecting {$this->type}." );
}
break;
}
}
}

?>
PHP übersetzt die PHP-Array-Syntax in Aufrufe der Methoden des ArrayAccess-Interfaces. Die Methode offsetExists() wird aufgerufen, sobald auf die Existenz eines Array-Schlüssels mit Hilfe vonisset() getestet wird. offsetGet(), wie erwartet, führt Lese-Zugriffe auf Array-Elemente aus. Parallel dazu ist offsetSet() für den Schreibzugriff zuständig und offsetUnset() für das löschen von Elementen beim Aufruf von unset(). Die verwendete Methode checkOffset() (Listing 7) überprüft einen gegebenen Array-Offset auf Validität. Wie beschrieben erwartet die Beispiel-Implementierung einen Integer Offset zwischen 0 und der Array-Länge –1. Werden diese Kriterien nicht eingehalten, wird wiederum die OutOfBoundsException aus der SPL Extension geworfen. Ähnlich funktioniert die Methode checkValue() für den an offsetSet() übergebenen Wert. Hier wird zunächst nach „normalen“ Typen und Objekten unterschieden. „Normale Typen“ sind hierbei Skalare, Ressourcen und Arrays. Ist der beim Erzeugen des RealArray-Objektes angegebene Typ von dieser Sorte, wird der Typ des übergebenen Wertes mit Hilfe der PHP-Funktion gettype()überprüft. Für alle weiteren Typen wird getestet, ob es sich beim übergebenen Wert um ein Objekt handelt und ob die Klasse mit dem Werte-Typ des RealArray-Objektes übereinstimmt. In beiden Fällen wird bei einem falschen Werte-Typ eine UnexpectedValueException ausgelöst, die ebenfalls in SPL Verfügbar ist. Letztendlich müssen noch die Methoden der Interfaces Iterator und Countable implementiert werden, damit sich Instanzen der RealArray-Klasse möglichst ähnlich eines normalen PHP-Arrays verhalten (folgendes Listing 9). Iterator stellt die Methoden bereit, mit denen PHP das Iterieren mittels der foreach-Kontrollstruktur erlaubt, wohingegen Countable in typischer Array-Manier die Verwendung von count() auf dem Objekt ermöglicht. Listing 9
array );
}

public function key()
{
return key( $this->array );
}

public function next()
{
return next( $this->array );
}

public function rewind()
{
return reset( $this->array );
}

public function valid()
{
return ( current( $this->array ) !== false );
}

public function count()
{
return count( $this->array );
}

// ...
}

?>
Die Methoden des Iterator-Interface sind beinahe alle analog zu PHP-Funktionen auf Arrays benannt: current() gibt das aktuelle Element zurück, next() setzt den Array-Pointer ein Element weiter und key() gibt den aktuellen Schlüssel zurück. Von der Namensgebung weicht lediglich rewind() ab, welches der Funktion reset() entspricht. Die Methode valid() hat kein PHP-Pendant und wird aufgerufen, um nach dem Umsetzen des Iterators via next() oder rewind() zu prüfen, ob ein Valides Element vorliegt. In der Beispiel-Implementierung werden alle Methoden entsprechend auf Funktionsaufrufe mit dem internen Daten-Array gemappt. Die einzige Methode des Countable Interface, passenderweise count() genannt, gibt die Anzahl der Array-Elemente zurück.

Limitierungen von Iterator und ArrayAccess

Achtung, auch wenn die Methoden des Iterator-Interfaces eine gewisse Ähnlichkeit zu den PHP-Array-Funktionen aufweisen heißt dies nicht, dass diese Funktionen mit Implementierungen des Interfaces funktionieren. Zum Beispiel resultiert der Aufruf voncurrent() auf einem Objekt, das Iterator implementiert nicht darin, dass die Methodecurrent() auf dem Objekt aufgerufen wird. Vielmehr wird hierbei das Objekt selbst wie ein Array benutzt und die Funktion gibt beim ersten Aufruf das erste, öffentliche Attribut zurück. Gleiches gilt für next() und reset(). Dieses Verhalten ist auf PHP Version 4 zurückzuführen, wo Objekte nicht mehr waren als Arrays, an die bestimmte Funktionen gebunden wurden. Leider wurde dieses Verhalten nicht geändert und damit wäre die Änderung durch das Iterator-Interface ein Bruch der Abwärtskompatibliltät. Bei der Implementierung eigener Projekte mit dem Iterator-Interface sollten Sie das bedenken. Letztendlich soll ein Beispiel für die nun fertige RealArray-Klasse nicht ausbleiben. Listing 10 zeigt die Einfache Erzeugung und Verwendung eines Integer-Arrays mit zehn Elementen. Zunächst wird ein neues Array erzeugt, dem anschließend zwei Elemente hinzugefügt werden. Die foreach-Schleife iteriert über diese beiden Elemente und gibt sie aus. Anschließend wird die Anzahl der Elemente im Integer-Array ausgegeben. Die letzten drei auskommentierten Zeilen würden zu Fehlern führen. In der ersten Zeile wird ein Index verwendet, der über die Länge des Arrays hinausgeht. Zeile zwei Benutzt einen gänzlich inkorrekten Index, einen String. Zuletzt wird in der dritten Zeile ein falscher Werte-Typ verwendet. Listing 10
 $val )
{
echo "$key => $valn";
}

var_dump( count( $intArr ) );

// $intArr[10] = 11;
// $intArr["foo"] = 23;
// $intArr[2] = array();

?>

Fazit

PHP ist durch seine schwache Typisierung einerseits komfortabel und mächtig, andererseits verleitet diese Eigenschaft zu unsauberer Programmierung, und diese ist dadurch oft anfällig für Flüchtigkeitsfehler. Dieser Artikel hat zwei Möglichkeiten vorgestellt, wie das Fehlerpotential effizient reduziert werden kann, ohne Einbußen bei der Flexibilität und Intuitivität hinnehmen zu müssen. Beide gezeigten Beispiele sind Abwandlungen von Programmier-Interfaces die innerhalb der Komponenten-Bibliothek eZ Components entwickelt wurden und aktiv verwendet werden. Im Source-Code dieser Bibliothek finden Sie bei Interesse noch weitere interessante Ansätze für komfortable und sichere OO-APIs in PHP. Hier der Link zu den eZ Components.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -