Grundkonzepte
PHP behandelt Objekte auf die gleiche Weise wie Referenzen und Handles, d. h. jede Variable enthält eine Referenz auf das Objekt und nicht eine Kopie des gesamten Objekts.
Wenn ein neues Objekt erstellt wird, wird dem Objekt immer ein Wert zugewiesen, es sei denn, das Objekt definiert einen Konstruktor und bei einem Fehler wird eine Ausnahme ausgelöst. Klassen sollten vor der Instanziierung definiert werden.
Wenn die Klasse beim Erstellen eines Objekts zu einem Namespace gehört, muss der vollständige Name verwendet werden.
Innerhalb einer Klassendefinition können Objekte mit new self
und new parent
erstellt werden.
<code><?php $instance = new stdClass(); $assigned = $instance; $reference = & $instance; $instance->var = '$assigned will have this value.'; $instance = null; var_dump($instance); var_dump($reference); var_dump($assigned);</code>
Die Ausgabe dieses Codes ist wie folgt.
<code>null null object(stdClass)[1] public 'var' => string '$assigned will have this value.' (length=31)</code>
PHP 5.3 führt zwei neue Methoden zum Erstellen einer Instanz eines Objekts ein. Sie können die folgenden Methoden zum Erstellen von Instanzen verwenden.
<code><?php class Test { static public function getNew() { return new static; } } class Child extends Test {} $obj1 = new Test(); $obj2 = new $obj1; var_dump($obj1 !== $obj2); // true $obj3 = Test::getNew(); var_dump($obj3 instanceof Test); // true $obj4 = Child::getNew(); var_dump($obj4 instanceof Child); // true var_dump($obj1 == $obj2); // true</code>
PHP unterstützt keine Mehrfachvererbung. Geerbte Methoden und Eigenschaften können durch Neudeklaration mit demselben Namen überschrieben werden. Beachten Sie, dass die Parameter konsistent bleiben müssen, mit Ausnahme des Konstruktors. Wenn die übergeordnete Klasse jedoch beim Definieren einer Methode final
verwendet, kann die Methode nicht überschrieben werden. Auf überschriebene Methoden und Eigenschaften kann über parent::
zugegriffen werden. Sie können nur auf Konstanten parent::
in der übergeordneten Klasse zugreifen, nicht jedoch auf Variablen. const
<code><?php class A { private $name = 'A'; const conname = 'A'; public function getName() { return $this->name; } } class B extends A { private $name = 'B'; const conname = 'B'; public function getName() { return $this->name; } public function getParent() { return parent::conname; } } class C extends B { private $name = 'C'; const conname = 'C'; public function getName() { return $this->name; } public function getParent() { return parent::conname; } } $a = new A; var_dump($a->getName()); // A $b = new B; var_dump($b->getName()); // B var_dump($b->getParent()); // A $c = new C; var_dump($c->getName()); // C var_dump($c->getParent()); // B</code>
auch zur Auflösung von Klassennamen verwendet werden. Mit class
können Sie eine Zeichenfolge erhalten, die den vollständig qualifizierten Namen der Klasse ClassName::class
enthält. ClassName
<code><?php namespace NS { class ClassName {} echo ClassName::class; // NS\ClassName }</code>
Attribute sind variable Mitglieder der Klasse. Variablen in Eigenschaften können initialisiert werden, der initialisierte Wert muss jedoch eine Konstante sein. Die Konstante bedeutet hier, dass das PHP-Skript seinen Wert während der Kompilierungsphase erhalten kann und für die Auswertung nicht auf Laufzeitinformationen angewiesen ist.
Verwenden Sie in der Member-Methode der Klasse
, um auf nicht statische Eigenschaften zuzugreifen, und verwenden Sie $this->property
, um auf statische Eigenschaften zuzugreifen. Verwenden Sie das Schlüsselwort self::$property
, wenn Sie statische Eigenschaften deklarieren. static
Klassenkonstanten
Das Symbol
und das Schlüsselwort für die Zugriffskontrolle sind beim Definieren von Konstanten nicht erforderlich. $
Klassen automatisch laden
Beim Schreiben objektorientierter Anwendungen ist es üblich, für jede Klassendefinition eine PHP-Quelldatei zu erstellen. Wenn eine Datei diese Klassen aufrufen muss, muss eine lange Liste der enthaltenen Dateien am Anfang der Datei geschrieben werden. Tatsächlich ist dies nicht erforderlich. Sie können eine
-Funktion definieren, die automatisch aufgerufen wird, wenn Sie versuchen, eine Klasse zu verwenden, die noch nicht definiert wurde. __autoload()
eine flexiblere Möglichkeit bietet, das automatische Laden von Klassen zu implementieren. Wir werden uns später damit befassen. spl_autoload_register()
Es können gefährliche Zeichen in der Benutzereingabe enthalten sein. Zumindest die Eingabe sollte überprüft werden, wenn
. __autoload()
<code><?php function __autoload($class_name) { require_once $class_name.'.php'; } $obj1 = new MyClass1(); $obj2 = new MyClass2();</code>
Konstruktor und Destruktor
PHP 5 ermöglicht Entwicklern, eine Methode als Konstruktor in einer Klasse zu definieren, und der Konstruktor unterstützt keine Überladung.
Wenn in der Unterklasse ein Konstruktor definiert ist, wird der Konstruktor der übergeordneten Klasse nicht implizit aufgerufen, andernfalls wird er wie eine normale Klassenmethode von der übergeordneten Klasse geerbt (vorausgesetzt, er ist nicht als
definiert). Um den Konstruktor der übergeordneten Klasse auszuführen, müssen Sie private
im Konstruktor der Unterklasse aufrufen. parent::__construct()
und die übergeordnete Klasse __construct()
unterschiedliche Parameter haben, können diese überschrieben werden. __construct()
Der Destruktor wird ausgeführt, wenn alle Verweise auf ein Objekt gelöscht werden oder das Objekt explizit zerstört wird. Der Destruktor wird auch dann aufgerufen, wenn das Skript mit
beendet wird. exit()
Zugriffskontrolle
Klassenattribute müssen als öffentlich, geschützt oder privat definiert werden und das Schlüsselwort darf nicht weggelassen werden. Wenn die Methode in der Klasse das Schlüsselwort für die Zugriffskontrolle nicht festlegt, ist die Methode standardmäßig öffentlich.
Objekte derselben Klasse können auf die privaten und geschützten Mitglieder des anderen zugreifen, auch wenn sie nicht dieselbe Instanz sind. Das Beispielprogramm lautet wie folgt.
<code><?php Class Test { private $foo; public function __construct($foo) { $this->foo = $foo; } private function bar() { echo 'Accessed the private method.'; } public function baz(Test $other) { $other->foo = 'hello'; var_dump($other->foo); $other->bar(); } } $test = new Test('test'); $test->baz(new Test('other'));</code>
Wenn eine Klasse eine andere erweitert, muss die übergeordnete Klasse vor der untergeordneten Klasse deklariert werden.
Bereichsauflösungsoperator
Der Bereichsauflösungsoperator ist, einfach ausgedrückt, ein Doppelpunktpaar, das für den Zugriff auf statische Mitglieder und Klassenkonstanten sowie zum Aufrufen von Eigenschaften und Methoden in übergeordneten Klassen verwendet werden kann.
Wenn Sie diese Elemente außerhalb der Klassendefinition referenzieren, verwenden Sie den Klassennamen.
statisch
Mit dem Schlüsselwort
können statische Methoden und Eigenschaften sowie statische Variablen und späte statische Bindung definiert werden. Wenn Sie eine Klasseneigenschaft oder Methode als „statisch“ deklarieren, können Sie direkt darauf zugreifen, ohne die Klasse zu instanziieren. static
Wenn keine Zugriffskontrolle angegeben ist, sind Eigenschaften und Methoden standardmäßig öffentlich.
Der Aufruf einer nicht statischen Methode von einer statischen Methode führt zu einem Fehler der Ebene
. E_STRICT
抽象类
PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。
抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。
方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。
<code><?php abstract class AbstractClass { abstract protected function prefixName($name); } class ConcreteClass extends AbstractClass { public function prefixName($name, $separator = ', ') { if($name === "Pacman") { $prefix = 'Mr'; } elseif($name === 'Pacwoman') { $prefix = "Mrs"; } else { $prefix = ''; } return "$prefix $separator $name "; } } $class = new ConcreteClass; echo $class->prefixName('Pacman'); echo $class->prefixName('Pacwoman');</code>
对象接口
听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。
接口也可以继承多个接口,用逗号分隔,使用extends
操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements
操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。
接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。
traits
从PHP 5.4.0开始,可以使用traits
实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。
优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof
操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as
操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。
<code><?php trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } } trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } } class Talker { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; B::bigTalk as talk; } } $t = new Talker(); $t->smallTalk(); // b $t->bigTalk(); // A $t->talk(); // B</code>
使用as
操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。
<code><?php trait HelloWorld { public function sayHello() { echo 'Hello World.'; } } class MyClass1 { use HelloWorld { sayHello as protected; } } class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }</code>
就像类能够使用trait
那样,多个trait
能够组合为一个trait
。
为了对使用的类施加强制要求,trait 支持抽象方法的使用。
<code><?php trait Hello { public function sayHelloWorld() { echo 'Hello ' . $this->getWorld(); } abstract public function getWorld(); } class MyHelloWorld { private $world; use Hello; public function getWorld() { return $this->world; } public function setWorld($val) { $this->world = $val; } } $c = new MyHelloWorld; $c->setWorld('world'); $c->sayHelloWorld();</code>
如果trait
定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。
重载
PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public
。
使用__get()
,__set()
,__isset()
,__unset()
进行属性重载,示例如下。
<code><?php class PropertyTest { private $data = array(); public $declared = 1; private $hidden = 2; public function __set($name, $value) { echo "Setting $name to $value. " . '<br>'; $this->data[$name] = $value; } public function __get($name) { echo "Getting $name. <br>"; if(array_key_exists($name, $this->data)) { return $this->data[$name]; } return null; } public function __isset($name) { echo "Is $name set? <br>"; return isset($this->data[$name]); } public function __unset($name) { echo "Unsetting $name. <br>"; unset($this->data[$name]); } } $obj = new PropertyTest; $obj->a = 1; var_dump($obj->a); var_dump(isset($obj->a)); unset($obj->a); var_dump(isset($obj->a)); var_dump($obj->declared); var_dump($obj->hidden);</code>
输出结果如下:
<code>Setting a to 1. Getting a. int 1 Is a set? boolean true Unsetting a. Is a set? boolean false int 1 Getting hidden. null</code>
在对象中调用一个不可访问方法时,__call()
会被调用。用静态方式中调用一个不可访问方法时,__callStatic()
会被调用。参数为调用方法的名称和一个枚举数组,注意区分大小写。
使用__call()
和__callStatic()
对方法重载,示例如下。
<code><?php class MethodTest { public function __call($name, $arguments) { echo "Calling object method $name " . implode(', ', $arguments) . '<br>'; } public static function __callStatic($name, $arguments) { echo "Calling static method $name " . implode(', ', $arguments) . '<br>'; } } $obj = new MethodTest; $obj->runTest('in object context'); MethodTest::runTest('in static context');</code>
遍历对象
对象可以用过单元列表来遍历,例如用foreach
语句。默认所有可见属性都将被用于遍历。
<code><?php class MyClass { public $var1 = 'value 1'; public $var2 = 'value 2'; public $var3 = 'value 3'; private $var4 = 'value 4'; protected $var5 = 'value 5'; } $obj = new MyClass; foreach($obj as $key => $value) { echo "$key => $value <br>"; }</code>
示例程序2实现了Iterator接口的对象遍历,示例程序3通过实现IteratorAggregate来遍历对象。
魔术方法
PHP 将所有以__
(两个下划线)开头的类方法保留为魔术方法。定义类方法时,除魔术方法外,建议不要以__
为前缀。
前面遇到过的魔术方法有:__construct()
,__destruct()
,__call()
,__callStatic()
,__get()
,__set()
,__isset()
,__unset()
。后面将会介绍:__sleep()
,__wakeup()
,__toString()
,__invoke()
,__set_state()
,__clone()
和__debugInfo()
。
__sleep
和__wakeup
不清楚具体做什么用的,示例程序中给出了个数据库连接的例子。
__toString
方法用于一个类被当成字符串时应怎样回应。此方法必须返回一个字符串,且不能再方法中抛出异常。如果将一个未定义__toString()
方法的对象转换为字符串,将产生错误。
当尝试以调用函数的方式调用一个对象时,__invoke()
方法会被调用。
当调用var_export()
导出类时,__set_state()
会被调用。
当调用var_dump()
时,__debugInfo
会被调用。PHP 5.6新加入,没合适的环境无法测试。
final
果父类中的方法被声明为final
,则子类无法覆盖该方法。如果一个类被声明为final
,则不能被继承。属性不能被定义为final
,只有类和方法才能被定义为final
。
对象复制
多数情况,我们不需要完全复制一个对象,但有时确实需要。对象复制可以通过clone
关键字来完成。这种复制是通过调用对象的__clone()
方法实现的,但是对象中的__clone()
方法不能被直接调用。
对象比较
比较运算符==
为真的条件是:两个对象的属性和属性值都相等,而且两个对象是同一个类的实例。
继承与统一个基类的两个子类的对象不会相等==
。
<code><?php class Base {} class A extends Base {} class B extends Base {} $a = new A; $b = new B; var_dump($a == $b); // false</code>
全等运算符===
为真的条件是:两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
类型约束
类型约束是指函数的参数可以指定必须为对象、接口、数组或者callable
类型。但是类型约束不能用于标量类型如int
或string
,traits
也不允许。类型约束允许NULL
值。
后期静态绑定
后期静态绑定,用于在继承范围内引用静态调用的类。
转发调用,指的是通过以下几种方式进行的静态调用:self::
,parent::
,static::
以及forward_static_call()
。
后期静态绑定的工作原理是,存储了上一个非转发调用的类名。
当进行静态方法调用时,该类名即为明确指定的那个;当进行非静态方法调用时,即为该对象所属的类。
使用self::
或者__CLASS__
对当前类的静态引用,取决于定义当前方法所在的类。
<code><?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); // A B::who(); // B</code>
用static::
关键字表示运行时最初调用的类,后期静态绑定就是这样使用。如下面程序所示,也就是说调用test()
时引用的类是B
而不是A
。
<code><?php class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); // B B::who(); // B</code>
示例2给出的是非静态环境下使用static::
。
后期静态绑定的解析,会一直到取得一个完全解析了的静态调用为止。另外,如果静态调用使用parent::
或self::
将转发调用信息。
<code><?php class A { public static function foo() { static::who(); } public static function who() { echo __CLASS__; } } class B extends A { public static function test() { A::foo(); parent::foo(); self::foo(); } public static function who() { echo __CLASS__; } } class C extends B { public static function who() { echo __CLASS__; } } C::test(); // ACC</code>
那么问题来了,结果为什么是这样的呢?
对象和引用
默认情况下,对象时通过引用传递的。但这种说法不完全正确,其实两个对象变量不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
对象序列化
所有PHP里面的值,都可以使用函数serialize()
来返回一个包含字节流的字符串来表示。unserialize()
函数能够重新把字符串变为原来的值。
序列化一个对象,将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()
一个对象,这个对象的类必须已经定义过。在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。
(全文完)
以上就介绍了类与对象 - PHP手册笔记,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。