Die Grundlagen
class
Einfache Klassendefinitionen beginnen mit dem Schlüsselwort
class
, gefolgt von einem Klassennamen, gefolgt von
einem Paar geschweifter Klammern, die die Definitionen der Eigenschaften
und Methoden der Klasse enthalten.
Der Klassenname kann ein beliebiger gültiger Bezeichner sein,
vorausgesetzt es ist kein von PHP
reserviertes Wort. Ein gültiger
Klassenname beginnt mit einem Buchstaben oder einem Unterstrich, gefolgt
von einer beliebigen Anzahl von Buchstaben, Ziffern oder Unterstrichen;
als regulärer Ausdruck formuliert:
^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
.
Eine Klasse darf aus ihren eigenen Konstanten, Variablen ("Eigenschaften" genannt) und Funktionen ("Methoden" genannt) bestehen.
Beispiel #1 Definition einer einfachen Klasse
<?php
class SimpleClass
{
// Deklaration einer Eigenschaft
public $var = 'ein Standardwert';
// Deklaration einer Methode
public function displayVar() {
echo $this->var;
}
}
?>
Die Pseudovariable $this ist verfügbar, wenn eine Methode aus einem Objektkontext heraus aufgerufen wird. $this ist eine Referenz auf das aufgerufene Objekt.
Wird eine nicht-statische Methode statisch aufgerufen, so wird ein Error ausgelöst. Vor PHP 8.0.0 führte dies zu einem Hinweis über das veraltete Verfahren und $this war nicht definiert.
Beispiel #2 Einige Beispiele für die Pseudovariable $this
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this ist definiert (';
echo get_class($this);
echo ").\n";
} else {
echo "\$this ist nicht definiert.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe mit PHP 7:
$this ist definiert (A). Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this ist nicht definiert. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this ist nicht definiert. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this ist nicht definiert.
Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:
$this ist definiert (A). Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 thrown in %s on line 27
new
Um eine Instanz einer Klasse zu erzeugen, muss das Schlüsselwort
new
verwendet werden. Ein Objekt wird immer erzeugt,
außer das Objekt besitzt einen definierten
Konstruktor, der aufgrund eines
Fehlers eine Exception auslöst.
Klassen sollten vor ihrer Instantiierung definiert werden (in manchen
Fällen ist dies eine Notwendigkeit).
Wenn ein String, der den Namen einer Klasse enthält, zusammen
mit new
verwendet wird, wird eine neue Instanz dieser
Klasse erzeugt. Falls sich die Klasse in einem Namensraum befindet, muss
der voll qualifizierte Name hierfür genutzt werden.
Hinweis:
Wenn dem Klassenkonstruktor keine Argumente übergeben werden müssen, können die Klammern hinter dem Klassennamen weggelassen werden.
Beispiel #3 Eine Instanz erzeugen
<?php
$instanz = new SimpleClass();
// dies ist auch mit einer Variablen möglich:
$klassenName = 'SimpleClass';
$instanz = new $klassenName(); // new SimpleClass()
?>
Seit PHP 8.0.0 wird die Verwendung von new
mit
beliebigen Ausdrücken unterstützt. Dies ermöglicht eine komplexere
Instanziierung, sofern der Ausdruck einen String erzeugt.
Die Ausdrücke müssen in Klammern eingeschlossen werden.
Beispiel #4 Erzeugen einer Instanz mit einem beliebigen Ausdruck
Hier werden mehrere Beispiele für gültige beliebige Ausdrücke gezeigt,
die einen Klassennamen erzeugen. Enthalten ist ein Funktionsaufruf, eine
String-Verkettung und die Konstante ::class
.
<?php
class ClassA extends \stdClass {}
class ClassB extends \stdClass {}
class ClassC extends ClassB {}
class ClassD extends ClassA {}
function getSomeClass(): string
{
return 'ClassA';
}
var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>
Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:
object(ClassA)#1 (0) { } object(ClassB)#1 (0) { } object(ClassC)#1 (0) { } object(ClassD)#1 (0) { }
Im Kontext einer Klasse ist es möglich, neue Objekte mit
new self
und new parent
anzulegen.
Wenn man eine bereits erzeugte Instanz einer Klasse einer neuen Variablen zuweist, wird die neue Variable auf dieselbe Instanz zugreifen wie das Objekt, das zugewiesen wurde. Dieses Verhalten ist dasselbe, wenn man Instanzen an Funktionen übergibt. Eine Kopie eines bereits erzeugten Objekts erhält man, indem man es klont.
Beispiel #5 Objektzuweisung
<?php
$instanz = new SimpleClass();
$zugewiesen = $instanz;
$referenz =& $instanz;
$instanz->var = '$zugewiesen wird diesen Wert haben';
$instanz = null; // $instanz und $referenz werden null
var_dump($instanz);
var_dump($referenz);
var_dump($zugewiesen);
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
NULL NULL object(SimpleClass)#1 (1) { ["var"]=> string(34) "$zugewiesen wird diesen Wert haben" }
Es gibt mehrere Möglichkeiten, Instanzen eines Objekts zu erzeugen:
Beispiel #6 Erzeugen neuer Objekte
<?php
class Test
{
static public function getNew()
{
return new static;
}
}
class Child extends Test
{}
$obj1 = new Test();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);
$obj3 = Test::getNew();
var_dump($obj3 instanceof Test);
$obj4 = Child::getNew();
var_dump($obj4 instanceof Child);
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
bool(true) bool(true) bool(true)
Es ist möglich, auf ein Mitglied eines neu erzeugten Objekts in einem einzigen Ausdruck zuzugreifen:
Beispiel #7 Zugriff auf ein Mitglied eines neu erzeugten Objekts
<?php
echo (new DateTime())->format('Y');
?>
Das oben gezeigte Beispiel erzeugt eine ähnliche Ausgabe wie:
2016
Hinweis: Vor PHP 7.1 werden die Argumente nicht ausgewertet, wenn keine Konstruktor-Funktion definiert ist.
Eigenschaften und Methoden
Klassen-Eigenschaften und -Methoden leben in separaten "Namensräumen", so dass es eine Eigenschaft und eine Methode desselben Namens geben kann. Der Zugriff auf eine Eigenschaft und eine Methode hat die gleiche Schreibweise und ob auf eine Eigenschaft zugegriffen oder eine Methode aufgerufen wird, hängt einzig und allein vom Kontext ab, d. h. ob die Verwendung ein Variablenzugriff oder ein Funktionsaufruf ist.
Beispiel #8 Variablenzugriff vs. Methodenaufruf
<?php
class Foo
{
public $bar = 'Eigenschaft';
public function bar() {
return 'Methode';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Eigenschaft Methode
Das bedeutet, dass der Aufruf einer anonymen Funktion, die einer Eigenschaft zugewiesen wurde, nicht direkt möglich ist. Stattdessen muss beispielsweise die Eigenschaft zunächst einer Variablen zugewiesen werden. Es ist möglich, eine solche Eigenschaft direkt aufzurufen, indem man sie in Klammern einschließt.
Beispiel #9 Aufruf einer anonymen Funktion, die in einer Eigenschaft gespeichert ist
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
42
extends
Eine Klasse kann die Konstanten, Methoden und Eigenschaften einer anderen
Klasse erben, indem man das Schlüsselwort extends
in
der Deklaration benutzt. Es ist nicht möglich, mehrere Klassen zu
erweitern; eine Klasse kann nur eine einzige Basisklasse beerben.
Die geerbten Konstanten, Methoden und Eigenschaften können überschrieben werden, indem sie mit demselben Namen neu deklariert werden, mit dem sie in der Elternklasse definiert wurden. Falls die Elternklasse eine Methode oder eine Konstante als final definiert hat, können diese nicht überschrieben werden. Es ist möglich, auf die überschriebenen Methoden oder statischen Eigenschaften zuzugreifen, wenn diese mittels parent:: referenziert werden.
Hinweis: Seit PHP 8.1.0 können Konstanten als final deklariert werden.
Beispiel #10 Einfache Vererbung
<?php
class ExtendClass extends SimpleClass
{
// Die Elternmethode überschreiben
function displayVar()
{
echo "Erweiternde Klasse\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Erweiternde Klasse ein Standardwert
Regeln zur Signaturkompatibilität
Wenn eine Methode überschrieben wird, so muss deren Signatur mit derjenigen
der Elternmethode kompatibel sein. Andernfalls wird ein fataler Fehler
hervorgerufen oder, vor PHP 8.0.0, eine Warnung der Stufe
E_WARNING
ausgegeben. Eine Signatur ist kompatibel,
wenn sie die Regeln zur Varianz
berücksichtigt, einen notwendigen Parameter optional macht und alle neuen
Parameter optional sind. Dies ist als das Liskovsche Substitutionsprinzip,
kurz LSP, bekannt. Der
Konstruktor sowie
private
Methoden sind von diesen Regeln zur
Signaturkompatibilität ausgenommen, weshalb im Fall einer Untimmigkeit in
der Signatur kein fataler Fehler hervorgerufen wird.
Beispiel #11 Kompatible Kindmethoden
<?php
class Base
{
public function foo(int $a) {
echo "Gültig\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Gültig Gültig
Das folgende Beispiel demonstriert, dass eine Kindmethode nicht zur Elternmethode kompatibel ist, wenn sie einen Parameter entfernt oder einen optionalen Parameter notwendig macht.
Beispiel #12 Fataler Fehler, wenn eine Kindmethode einen Parameter entfernt
<?php
class Base
{
public function foo(int $a = 5) {
echo "Gültig\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
Das oben gezeigte Beispiel erzeugt mit PHP 8 eine ähnliche Ausgabe wie:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Beispiel #13 Fataler Fehler, wenn eine Kindmethode einen optionalen Parameter notwendig macht
<?php
class Base
{
public function foo(int $a = 5) {
echo "Gültig\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
Das oben gezeigte Beispiel erzeugt mit PHP 8 eine ähnliche Ausgabe wie:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Die Umbenennung eines Parameters einer Methode in einer Kindklasse führt nicht zur Inkompatibilität. Es wird jedoch davon abgeraten, da es zu Fehlern der Stufe Error führt, wenn benannte Parameter verwendet werden.
Beispiel #14 Fehler, wenn benannte Parameter verwendet und diese in der Kindklasse umbenannt werden
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Übergabe der Parameter gemäß den Konventionen von A::test()
$obj->test(foo: "foo", bar: "bar"); // ERROR!
Das oben gezeigte Beispiel erzeugt eine ähnliche Ausgabe wie:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 thrown in /in/XaaeN on line 14
::class
Das Schlüsselwort class
kann auch für die
Namensauflösung einer Klasse verwendet werden. Um den vollständig
qualifizierten Namen der Klasse ClassName
zu erhalten,
kann ClassName::class
verwendet werden. Dies ist vor
allem dann praktisch, wenn mit
Namensräumen gearbeitet wird.
Beispiel #15 Auflösung von Klassennamen
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
NS\ClassName
Hinweis:
Bei der Auflösung des Klassennamens unter Verwendung von
::class
handelt es sich um eine Transformation zur Übersetzungszeit. Das bedeutet, dass zu der Zeit, zu der die Klassennamen-Zeichenkette erzeugt wird, noch kein Autoloading erfolgt ist. Daraus folgt, dass Klassennamen erweitert werden, selbst wenn die Klasse nicht existiert. In diesem Fall wird kein Fehler erzeugt.Beispiel #16 Fehlende Auflösung des Klassennamens
<?php
print Does\Not\Exist::class;
?>Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
Does\Not\Exist
Von PHP 8.0.0 an kann die Konstante ::class
auch auf
Objekte angewendet werden. Diese Auflösung des Klassennamens erfolgt nicht
zur Übersetzungszeit, sondern zur Laufzeit. Sie hat die gleichen
Auswirkungen wie der Aufruf von get_class() auf das
Objekt.
Beispiel #17 Namensauflösung eines Objekts
<?php
namespace NS {
class ClassName {
}
}
$c = new ClassName();
print $c::class;
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
NS\ClassName
Nullsafe-Methoden und -Eigenschaften
Von PHP 8.0.0 an kann auf Eigenschaften und Methoden stattdessen auch mit
dem "Nullsafe"-Operator zugegriffen werden: ?->
. Der
Nullsafe-Operator funktioniert genauso als Eigenschafts- oder
Methodenzugriff wie oben, mit dem Unterschied, dass null
zurückgegeben
wird, statt dass eine Exception erzeugt wird, wenn das Objekt, das
dereferenziert wird, null
ist. Wenn die Dereferenzierung Teil einer
Zeichenkette ist, wird der Rest der Zeichenkette übersprungen.
Der Effekt ist ähnlich, wie wenn man jeden Zugriff zuerst mit is_null() prüft, aber kompakter.
Beispiel #18 Nullsafe Operator
<?php
// Von PHP 8.0.0 an entspricht diese Zeile:
$result = $repository?->getUser(5)?->name;
// dem folgenden Codeblock:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
Hinweis:
Der Nullsafe-Operator wird am besten verwendet, wenn Null ein gültiger und potenziell erwarteter möglicher Rückgabewert für eine Eigenschaft oder eine Methode ist. Um einen Fehler anzuzeigen, ist die Erzeugung einer Exception vorzuziehen.