Auf Klasse oder Interface prüfen (Thema: PHP Beispiele)

In PHP Klassen darauf prüfen, ob sie Kindklassen einer anderen Klasse sind oder ein Interface implementieren

URL: http://www.rither.de/a/informatik/php-beispiele/klassen/auf-klasse-oder-interface-pruefen/

1. Prüfen mit instanceof

In einigen Fällen ist es notwendig, im Verlauf eines Skripts genauer zu bestimmen, ob sich ein Objekt von einer bestimmten Klasse ableitet oder ein ebenso bestimmtes Interface implementiert. Dieser Test wird mit dem instanceof-Operator durchgeführt. Die Syntax lautet $objekt instanceof Klasenname oder $objekt instanceof Interfacename. Sowohl für Klassen als auch Interfaces wird also der selbe Operator verwendet. Der Klassename/Interfacename darf nicht von einfachen oder doppelten Anführungszeichen umgeben sein. Zudem muss er immer rechts vom Operator stehen. Links steht entsprechend die Objektinstanz (typischerweise als Variable).

1.1. Kurzes Beispiel

Das einfachstmögliche, sinnvolle Beispiel lautet wohl:

PHP-Code: Mit instanceof auf stdClass testen
<?php
	$obj = new stdClass();
	if ($obj instanceof stdClass) {
		echo('$obj ist eine Instanz der Klasse stdClass!');
	} else {
		echo('$obj ist _keine_ Instanz der Klasse stdClass!');
	}
?>

HTML-Code: Ausgabe
$obj ist eine Instanz der Klasse stdClass!

1.2. Etwas umfangreicheres Beispiel

In diesem Beispiel werden zwei Klassen („MyClass” und „MyClassWithInterface”), sowie ein Interface „MyInterface” definiert. „MyClass” erweitert keine andere Klasse und bindet auch kein Interface ein. „MyClassWithInterface” hingegen implementiert das bereits erwähnte Interface „MyInterface”. Es wird nun jeweils von MyClass ($myClass) und MyClassWithInterface ($myClassWithInterface) ein Objekt erzeugt. Beide werden nacheinander per instanceof darauf geprüft, ob sie Instanzen der Klassen oder Interfaces MyClass, MyClassWithInterface, MyInterface oder stdClass sind.

PHP-Code: Mit instanceof prüfen, ob Objekte Instanzen von bestimmten Klassen sind oder Interfaces implementieren
<?php
	// Ein Beispielinterface
	interface MyInterface {
		// leer, da nur Beispiel
	}

	// Eine Beispielklasse
	class MyClass {
		// leer, da nur Beispiel
	}

	// Eine Beispielklasse, die das Beispielinterface einbindet
	class MyClassWithInterface implements MyInterface {
		// leer, da nur Beispiel
	}

	
	$myClass = new MyClass();
	$myClassWithInterface = new MyClassWithInterface();

	// Mit "instanceof" kann geprueft werden, ob sich ein Objekt von
	// einer bestimmten Klasse ableitet oder genau diese Klasse ist oder
	// ein bestimmtes Interface einbindet.
	var_dump($myClass instanceof MyClass); // true
	var_dump($myClass instanceof MyClassWithInterface); // false
	var_dump($myClass instanceof MyInterface); // false
	var_dump($myClass instanceof stdClass); // false

	var_dump($myClassWithInterface instanceof MyClass); // false
	var_dump($myClassWithInterface instanceof MyClassWithInterface); // true
	var_dump($myClassWithInterface instanceof MyInterface); // true
	var_dump($myClassWithInterface instanceof stdClass); // false

?>

HTML-Code: Ausgabe
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(false)


1.3. instanceof verlangt nicht, dass die Klassen/Interfaces existieren

Die auf der rechten Seite von instanceof angegebene Klasse bzw. das angegebene Interface muss NICHT existieren. PHP nimmt anstandslos einen Namen an, den es nicht kennt und zu dem auch nirgendwo Code eine Klasse existiert. Das heißt etwas direkter: Wenn man irgendeinen Vertipper im Namen der Klasse oder des Interfaces hat, dann wird PHP bei Erreichen des instanceof-Operators KEINEN Fehler, KEINE Warnung und KEINEN Notice generieren. Also besser jedes instanceof auf Fehler überprüfen. Das gilt auch für eventuell verwendete Namespaces.

PHP-Code: instanceof erzeugt Fehlermeldung bei nicht existierenden Klassen
<?php
	namespace Planes {
		class Boeing747 { }
	}

	// globaler Namespace
	namespace {
		$plane = new planes\Boeing747();
	
		// Vertipper
		if ($plane instanceof planes\Beoing747) {
			echo("Das ist eine Boeing 747!\n");
		} else {
			echo("Keine Boeing 747!\n");
		}
		
		// Namespace vergessen
		if ($plane instanceof Beoing747) {
			echo("Das ist eine Boeing 747!\n");
		} else {
			echo("Keine Boeing 747!\n");
		}
		
		// Nur das funktioniert
		// Man beachte, dass sich PHP nicht an der falschen Groß-/Kleinschreibung
		// des Namespaces stört.
		if ($plane instanceof planes\Boeing747) {
			echo("Das ist eine Boeing 747!\n");
		} else {
			echo("Keine Boeing 747!\n");
		}
	}
?>

HTML-Code: Ausgabe
Keine Boeing 747!
Keine Boeing 747!
Das ist eine Boeing 747!


2. Mit Reflection auf Klasse testen

Eine weitere Möglichkeit auf bestimmte Klassen zu testen ist es, Reflection zu verwenden. Dazu wird vom zu analysierenden Objekt eine ReflectionClass erzeugt, welche wiederum etliche Methoden anbietet, um das übergebene Objekt auszuwerten. An dieser Stelle ist allerdings nur isSubclassOf($className) interessant. Entsprechend des Namens gibt diese Methode zurück, ob das Objekt zu einer Unterklasse der Klasse mit Namen $className gehört.

PHP-Code: Eine ReflectionClass eines Objekts erzeugen und auf eine Elternklasse untersuchen
<?php
	interface MyInterface { }
	class MyClass { }
	class MyClassWithInterface implements MyInterface { }
	
	// Objekt wird erzeugt und an ReflectionClass übergeben
	$obj = new MyClassWithInterface();
	$refl = new ReflectionClass($obj);
	var_dump($refl->isSubclassOf('MyClass'));
	var_dump($refl->isSubclassOf('MyInterface'));
	var_dump($refl->isSubclassOf('stdClass'));
?>

HTML-Code: Ausgabe
bool(false)
bool(true)
bool(false)


Im Gegensatz zu instanceof wirft ReflectionClass eine Exception, wenn die an isSubclassOf() übergebene Klasse unbekannt ist. Leider ist ReflectionClass auch gleichzeitig wesentlich langsamer als instanceof und benötigt mehr Zeilen Code in der Anwendung.

3. Für Methodenparameter Klassen/Interfaces vorschreiben

Funktionen/Methoden können vorschreiben, von welcher Klasse sich ihre Parameter ableiten oder welche Interfaces sie implementieren müssen. Dazu muss lediglich der gewünschte Klassen- oder Interfacename vor dem jeweiligen Parameter der Funktion/Methode geschrieben werden. Er darf nicht von einfachen oder doppelten Anführungszeichen umgeben sein. PHP erzeugt einen „catchable fatal error”, wenn nicht die erwartete Klasse/das erwartete Interface übergeben wird. Anders als der Name vermuten lässt, kann man diesen nicht (einfach) per try-catch abfangen.

Achtung: Auch hier wird sich PHP nicht beschweren, wenn der eingegebene Klassen- oder Interfacename unbekannt ist. Fehler werden dann erst entstehen, wenn man versucht, ein eigentlich passendes Objekt zu übergeben.

Hinweis: Diese Vorgaben lassen sich ansonsten nur noch auf Arrays als erwarteter Parameter anwenden. Das erzwingen von Integern, Strings oder ähnlichem ist nicht möglich.

PHP-Code: Die Klassen oder implementierten Interfaces von Methodenparametern vorschreiben
<?php
	// Auch Funktionen/Methoden dürfen bereits in den Parametern vorschreiben,
	// welche Klassen übergeben werden dürfen
	function doSomething(MyClass $mc) {
		echo("Aufgerufen!\n\n");
	}
	
	
	interface MyInterface { }
	class MyClass { }
	class MyClassWithInterface implements MyInterface { }
	
	$myClass = new MyClass();
	$myClassWithInterface = new MyClassWithInterface();
	
	
	doSomething($myClass); // geht, da $myClass ein Objekt der Klasse MyClass ist
	doSomething($myClassWithInterface); // erzeugt einen Fatal Error, da die Funktion nur mit MyClass aufgerufen werden darf
?>

HTML-Code: Ausgabe
Aufgerufen!

<br />
<b>Catchable fatal error</b>:  Argument 1 passed to doSomething() must be an instance of MyClass, instance of MyClassWithInterface given, called in ...\test.php on line 18 and defined in <b>...\test.php</b> on line <b>4</b><br />


Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen zu Cookies erhalten Sie in unserer Datenschutzerklärung. OK