簡介
PHP 對待對象的方式與引用和句柄相同峦剔,即每個變量都持有對象的引用斩松,而不是整個對象的拷貝鹅搪。
基本概念
類的定義:
<?php
class SimpleClass
{
// property declaration
public $var = 'a default value';
// method declaration
public function displayVar() {
echo $this->var;
}
}
?>
類的實例化:
<?php
$instance = new SimpleClass();
// 也可以這樣做:
$className = 'Foo';
$instance = new $className(); // Foo()
?>
<?php
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance and $reference become null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
輸出:
NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
string(30) "$assigned will have this value"
}
繼承
PHP不支持多重繼承,一個類只能繼承一個基類贴唇。
被繼承的方法和屬性可以通過用同樣的名字重新聲明被覆蓋。但是如果父類定義方法時使用了 final飞袋,則該方法不可被覆蓋戳气。可以通過 parent:: 來訪問被覆蓋的方法或?qū)傩浴?br>
當覆蓋方法時巧鸭,參數(shù)必須保持一致否則 PHP 將發(fā)出 E_STRICT 級別的錯誤信息瓶您。但構(gòu)造函數(shù)例外,構(gòu)造函數(shù)可在被覆蓋時使用不同的參數(shù)纲仍。
::class
自 PHP 5.5 起呀袱,關(guān)鍵詞 class 也可用于類名的解析。使用 ClassName::class 你可以獲取一個字符串郑叠,包含了類 ClassName 的完全限定名稱夜赵。這對使用了 命名空間 的類尤其有用。
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
輸出:
NS\ClassName
屬性
屬性中的變量可以初始化乡革,但是初始化的值必須是常數(shù)寇僧,這里的常數(shù)是指 PHP 腳本在編譯階段時就可以得到其值,而不依賴于運行時的信息才能求值沸版。
如果直接使用 var 聲明屬性嘁傀,而沒有用 public,protected 或 private 之一视粮,PHP 5 會將其視為 public细办。
在類的成員方法里面,可以用 ->(對象運算符):$this->property(其中 property 是該屬性名)這種方式來訪問非靜態(tài)屬性馒铃。靜態(tài)屬性則是用 ::(雙冒號):self::$property 來訪問蟹腾。
<?php
class SimpleClass
{
// 錯誤的屬性聲明
public $var1 = 'hello ' . 'world';
public $var2 = <<<EOD
hello world
EOD;
public $var3 = 1+2;
public $var4 = self::myStaticMethod();
public $var5 = $myVar;
// 正確的屬性聲明
public $var6 = myConstant;
public $var7 = array(true, false);
//在 PHP 5.3.0 及之后痕惋,下面的聲明也正確
public $var8 = <<<'EOD'
hello world
EOD;
}
?>
類常量
可以把在類中始終保持不變的值定義為常量。在定義和使用常量的時候不需要使用 $ 符號娃殖。
常量的值必須是一個定值值戳,不能是變量,類屬性炉爆,數(shù)學運算的結(jié)果或函數(shù)調(diào)用堕虹。
接口(interface)中也可以定義常量。
自 PHP 5.3.0 起芬首,可以用一個變量來動態(tài)調(diào)用類赴捞。但該變量的值不能為關(guān)鍵字(如 self,parent 或 static)郁稍。
<?php
class MyClass
{
const constant = 'constant value';
function showConstant() {
echo self::constant . "\n";
}
}
echo MyClass::constant . "\n";
$classname = "MyClass";
echo $classname::constant . "\n"; // 自 5.3.0 起
$class = new MyClass();
$class->showConstant();
echo $class::constant."\n"; // 自 PHP 5.3.0 起
?>
構(gòu)造函數(shù)和析構(gòu)函數(shù)
構(gòu)造函數(shù)
void __construct ([ mixed $args [, $... ]] )
如果子類中定義了構(gòu)造函數(shù)則不會隱式調(diào)用其父類的構(gòu)造函數(shù)赦政。要執(zhí)行父類的構(gòu)造函數(shù),需要在子類的構(gòu)造函數(shù)中調(diào)用 parent::__construct()耀怜。如果子類沒有定義構(gòu)造函數(shù)則會如同一個普通的類方法一樣從父類繼承(假如沒有被定義為 private 的話)恢着。
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}
class OtherSubClass extends BaseClass {
// inherits BaseClass's constructor
}
// In BaseClass constructor
$obj = new BaseClass();
// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();
// In BaseClass constructor
$obj = new OtherSubClass();
?>
為了實現(xiàn)向后兼容性,如果 PHP 5 在類中找不到 __construct() 函數(shù)并且也沒有從父類繼承一個的話财破,它就會嘗試尋找舊式的構(gòu)造函數(shù)掰派,也就是和類同名的函數(shù)。因此唯一會產(chǎn)生兼容性問題的情況是:類中已有一個名為 __construct() 的方法卻被用于其它用途時左痢。
與其它方法不同靡羡,當 __construct() 被與父類 __construct() 具有不同參數(shù)的方法覆蓋時,PHP 不會產(chǎn)生一個 E_STRICT 錯誤信息俊性。
自 PHP 5.3.3 起略步,在命名空間中,與類名同名的方法不再作為構(gòu)造函數(shù)磅废。這一改變不影響不在命名空間中的類纳像。
析構(gòu)函數(shù)
void __destruct ( void )
PHP 5 引入了析構(gòu)函數(shù)的概念荆烈,這類似于其它面向?qū)ο蟮恼Z言拯勉,如 C++。析構(gòu)函數(shù)會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執(zhí)行憔购。
<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Destroying " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
?>
和構(gòu)造函數(shù)一樣宫峦,父類的析構(gòu)函數(shù)不會被引擎暗中調(diào)用。要執(zhí)行父類的析構(gòu)函數(shù)玫鸟,必須在子類的析構(gòu)函數(shù)體中顯式調(diào)用 parent::__destruct()导绷。此外也和構(gòu)造函數(shù)一樣,子類如果自己沒有定義析構(gòu)函數(shù)則會繼承父類的屎飘。
析構(gòu)函數(shù)即使在使用 exit() 終止腳本運行時也會被調(diào)用妥曲。在析構(gòu)函數(shù)中調(diào)用 exit() 將會中止其余關(guān)閉操作的運行贾费。
試圖在析構(gòu)函數(shù)(在腳本終止時被調(diào)用)中拋出一個異常會導致致命錯誤。
訪問控制
對屬性或方法的訪問控制檐盟,是通過在前面添加關(guān)鍵字 public(公有)褂萧,protected(受保護)或 private(私有)來實現(xiàn)的。被定義為公有的類成員可以在任何地方被訪問葵萎。被定義為受保護的類成員則可以被其自身以及其子類和父類訪問导犹。被定義為私有的類成員則只能被其定義所在的類訪問。
類屬性必須定義為公有羡忘,受保護谎痢,私有之一。如果用 var 定義卷雕,則被視為公有节猿。
類中的方法可以被定義為公有,私有或受保護漫雕。如果沒有設(shè)置這些關(guān)鍵字沐批,則該方法默認為公有。
其它對象的訪問控制
同一個類的對象即使不是同一個實例也可以互相訪問對方的私有與受保護成員蝎亚。這是由于在這些對象的內(nèi)部具體實現(xiàn)的細節(jié)都是已知的九孩。
<?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)
{
// We can change the private property:
$other->foo = 'hello';
var_dump($other->foo);
// We can also call the private method:
$other->bar();
}
}
$test = new Test('test');
$test->baz(new Test('other'));
?>
輸出:
string(5) "hello"
Accessed the private method.
對象繼承
除非使用了自動加載,否則一個類必須在使用之前被定義发框。如果一個類擴展了另一個躺彬,則父類必須在子類之前被聲明。此規(guī)則適用于類繼承其它類與接口梅惯。
<?php
class foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}
public function printPHP()
{
echo 'PHP is great.' . PHP_EOL;
}
}
class bar extends foo
{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}
$foo = new foo();
$bar = new bar();
$foo->printItem('baz'); // Output: 'Foo: baz'
$foo->printPHP(); // Output: 'PHP is great'
$bar->printItem('baz'); // Output: 'Bar: baz'
$bar->printPHP(); // Output: 'PHP is great'
?>
范圍解析操作符(::)
范圍解析操作符(也可稱作 Paamayim Nekudotayim)或者更簡單地說是一對冒號宪拥,可以用于訪問靜態(tài)成員,類常量铣减,還可以用于覆蓋類中的屬性和方法她君。
在類的外部使用 :: 操作符
<?php
class MyClass {
const CONST_VALUE = 'A constant value';
}
$classname = 'MyClass';
echo $classname::CONST_VALUE; // 自 PHP 5.3.0 起
echo MyClass::CONST_VALUE;
?>
在類定義內(nèi)部使用 ::
self
,parent
和 static
這三個特殊的關(guān)鍵字是用于在類定義的內(nèi)部對其屬性或方法進行訪問的葫哗。
<?php
class OtherClass extends MyClass
{
public static $my_static = 'static var';
public static function doubleColon() {
echo parent::CONST_VALUE . "\n";
echo self::$my_static . "\n";
}
}
$classname = 'OtherClass';
echo $classname::doubleColon(); // 自 PHP 5.3.0 起
OtherClass::doubleColon();
?>
當一個子類覆蓋其父類中的方法時缔刹,PHP 不會調(diào)用父類中已被覆蓋的方法。是否調(diào)用父類的方法取決于子類劣针。這種機制也作用于構(gòu)造函數(shù)和析構(gòu)函數(shù)校镐,重載以及魔術(shù)方法。
<?php
class MyClass
{
protected function myFunc() {
echo "MyClass::myFunc()\n";
}
}
class OtherClass extends MyClass
{
// 覆蓋了父類的定義
public function myFunc()
{
// 但還是可以調(diào)用父類中被覆蓋的方法
parent::myFunc();
echo "OtherClass::myFunc()\n";
}
}
$class = new OtherClass();
$class->myFunc();
?>
Static(靜態(tài))關(guān)鍵字
聲明類屬性或方法為靜態(tài)捺典,就可以不實例化類而直接訪問鸟廓。靜態(tài)屬性不能通過一個類已實例化的對象來訪問(但靜態(tài)方法可以)。
由于靜態(tài)方法不需要通過對象即可調(diào)用,所以偽變量 $this 在靜態(tài)方法中不可用引谜。
靜態(tài)屬性不可以由對象通過 -> 操作符來訪問牍陌。
用靜態(tài)方式調(diào)用一個非靜態(tài)方法會導致一個 E_STRICT 級別的錯誤。
抽象類
PHP 5 支持抽象類和抽象方法员咽。定義為抽象的類不能被實例化呐赡。任何一個類,如果它里面至少有一個方法是被聲明為抽象的骏融,那么這個類就必須被聲明為抽象的链嘀。被定義為抽象的方法只是聲明了其調(diào)用方式(參數(shù)),不能定義其具體的功能實現(xiàn)档玻。
繼承一個抽象類的時候怀泊,子類必須定義父類中的所有抽象方法;另外误趴,這些方法的訪問控制必須和父類中一樣(或者更為寬松)霹琼。此外方法的調(diào)用方式必須匹配,即類型和所需參數(shù)數(shù)量必須一致凉当。例如枣申,子類定義了一個可選參數(shù),而父類抽象方法的聲明里沒有看杭,則兩者的聲明并無沖突忠藤。 這也適用于 PHP 5.4 起的構(gòu)造函數(shù)。在 PHP 5.4 之前的構(gòu)造函數(shù)聲明可以不一樣的楼雹。
<?php
abstract class AbstractClass
{
// 強制要求子類定義這些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{
public function getValue() {
return "ConcreteClass2";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>
輸出:
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
<?php
abstract class AbstractClass
{
// 我們的抽象方法僅需要定義需要的參數(shù)
abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass
{
// 我們的子類可以定義父類簽名中不存在的可選參數(shù)
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"), "\n";
echo $class->prefixName("Pacwoman"), "\n";
?>
輸出:
Mr. Pacman
Mrs. Pacwoman
對象接口
使用接口(interface)模孩,可以指定某個類必須實現(xiàn)哪些方法,但不需要定義這些方法的具體內(nèi)容贮缅。
接口是通過 interface 關(guān)鍵字來定義的榨咐,就像定義一個標準的類一樣,但其中定義所有的方法都是空的谴供。
接口中定義的所有方法都必須是公有块茁,這是接口的特性。
實現(xiàn)(implements)
要實現(xiàn)一個接口桂肌,使用 implements 操作符数焊。類中必須實現(xiàn)接口中定義的所有方法,否則會報一個致命錯誤轴或。類可以實現(xiàn)多個接口昌跌,用逗號來分隔多個接口的名稱。
實現(xiàn)多個接口時照雁,接口中的方法不能有重名。
接口也可以繼承,通過使用 extends 操作符饺蚊。
類要實現(xiàn)接口萍诱,必須使用和接口中所定義的方法完全一致的方式。否則會導致致命錯誤污呼。
常量
接口中也可以定義常量裕坊。接口常量和類常量的使用完全相同,但是不能被子類或子接口所覆蓋燕酷。
<?php
// 聲明一個'iTemplate'接口
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// 實現(xiàn)接口
// 下面的寫法是正確的
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
// 下面的寫法是錯誤的籍凝,會報錯,因為沒有實現(xiàn) getHtml():
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>
<?php
interface a
{
public function foo();
}
interface b extends a
{
public function baz(Baz $baz);
}
// 正確寫法
class c implements b
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// 錯誤寫法會導致一個致命錯誤
class d implements b
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
?>
<?php
interface a
{
public function foo();
}
interface b
{
public function bar();
}
interface c extends a, b
{
public function baz();
}
class d implements c
{
public function foo()
{
}
public function bar()
{
}
public function baz()
{
}
}
?>
<?php
interface a
{
const b = 'Interface constant';
}
// 輸出接口常量
echo a::b;
// 錯誤寫法苗缩,因為常量不能被覆蓋饵蒂。接口常量的概念和類常量是一樣的。
class b implements a
{
const b = 'Class constant';
}
?>
重載
PHP所提供的"重載"(overloading)是指動態(tài)地"創(chuàng)建"類屬性和方法酱讶。我們是通過魔術(shù)方法(magic methods)來實現(xiàn)的退盯。
所有的重載方法都必須被聲明為 public。
這些魔術(shù)方法的參數(shù)都不能通過引用傳遞泻肯。
PHP中的"重載"與其它絕大多數(shù)面向?qū)ο笳Z言不同渊迁。傳統(tǒng)的"重載"是用于提供多個同名的類方法,但各方法的參數(shù)類型和個數(shù)不同灶挟。
遍歷對象
PHP 5 提供了一種定義對象的方法使其可以通過單元列表來遍歷琉朽,例如用 foreach 語句。默認情況下稚铣,所有可見屬性都將被用于遍歷漓骚。