- final
- var
- const常量
- 類自動加載
- 構造函數與析構函數
- 范圍解析操作符
- 關于抽象類和接口
- Trait
- 匿名類
- 魔術方法
- clone
- static::后期靜態綁定
- 對象與引用及傳址賦值與引用賦值
原文:PHP基礎參考10-類與對象語法 - 9ong
PHP基礎參考11-引用語法 - 9ong
很多人都停留在php是過程語言,不能面向對象,php已經8了,在5就已經逐步與面向對象接軌了。
PHP 具有完整的對象模型。特性包括: 訪問控制,抽象類和 final 類與方法,附加的魔術方法,接口,對象復制。
對于面向對象的基本知識,不需要我們介紹了,大家都懂。
final
被繼承的方法和屬性可以通過用同樣的名字重新聲明被覆蓋。但是如果父類定義方法時使用了 final,則該方法不可被覆蓋。可以通過 parent:: 來訪問被覆蓋的方法或屬性。
注意: 屬性不能被定義為 final,只有類和方法才能被定義為 final。
基本的知識,但很少人能用上,也許是場景不需要用到,但也許哪天就真香,時刻牢記。
var
雖然有些版本允許使用var定義屬性,會被視為public,但我們不建議使用。
const常量
常量的值必須是一個定值,不能是變量,類屬性,數學運算的結果或函數調用。
要區別于外部const/define定義的常量。
PHP 7.1.0 開始,類的常量可以定義為公有、私有或受保護。如果沒有設置這些關鍵字,則該常量默認為公有,也就是之前的版本只能使用const關鍵字定義常量,默認這些常量是公開可訪問的,7.1之后允許public、protect、private的訪問控制關鍵字。
類自動加載
現代php框架都引入自動加載機制了,特別是composer第三方庫使用。
spl_autoload_register() 函數可以注冊任意數量的自動加載器,當使用尚未被定義的類(class)和接口(interface)時自動去加載。
不建議使用__autoload函數,后續版本會被棄用。
spl_autoload_register函數用于注冊觸發加載類,提供回調加載,達到按需加載。
spl_autoload_register(function ($class_name) {
require_once $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
構造函數與析構函數
子類的構造函數不會默認或隱性的調用父類構造函數,有需要時,要手動調用父類構造函數。
析構函數即使在使用 exit() 終止腳本運行時也會被調用。但在析構函數中調用 exit() 將會中止其余關閉操作的運行。
在析構函數(在腳本終止時被調用)中拋出一個異常會導致致命錯誤。
范圍解析操作符
范圍解析操作符 ::
只是認識下這個符號叫:范圍解析操作符
在訪問類常量、靜態屬性、靜態方法,我們常會用到雙冒號,也就是范圍解析操作符。
關于抽象類和接口
如果對抽象類和接口有不清楚的可以看官方文檔介紹
Trait
Trait 是為類似 PHP 的單繼承語言而準備的一種代碼復用機制。Trait 為了減少單繼承語言的限制,使開發人員能夠自由地在不同層次結構內獨立的類中復用 method。
Trait 和 Class 相似,但僅僅旨在用細粒度和一致的方式來組合功能。 無法通過 trait 自身來實例化。它為傳統繼承增加了水平特性的組合;也就是說,應用的幾個 Class 之間不需要繼承。
-
優先順序是當前類中的方法會覆蓋 trait 方法,而 trait 方法又覆蓋了基類中的方法。
class Base { public function sayHello() { echo 'Hello '; } } trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class MyHelloWorld extends Base { use HelloWorld; } $o = new MyHelloWorld(); $o->sayHello(); //輸出:Hello World! //因為trait覆蓋了基類的方法 class TheWorldIsNotEnough { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $o = new TheWorldIsNotEnough(); $o->sayHello(); //輸出:Hello Universe! //因為當前類覆蓋trait的方法
一個類中支持use多個trait,用逗號隔開
-
方法沖突解決
如果兩個 trait 都插入了一個同名的方法,如果沒有明確解決沖突將會產生一個致命錯誤。
為了解決多個 trait 在同一個類中的命名沖突,需要使用 insteadof 操作符來明確指定使用沖突方法中的哪一個。
以上方式僅允許排除掉其它方法,as 操作符可以 為某個方法引入別名。 注意,as 操作符不會對方法進行重命名,也不會影響其方法。
use A, B { B::smallTalk insteadof A;//相當于排除A中的smallTalk A::bigTalk insteadof B; B::bigTalk as talk;//定義了talk別名 }
-
修改use trait中的方法訪問控制
直接看范例:
// 修改 sayHello 的訪問控制 class MyClass1 { use HelloWorld { sayHello as protected; } } // 給方法一個改變了訪問控制的別名 // 原版 sayHello 的訪問控制則沒有發生變化 class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }
-
trait組合
trait和class一樣也可以組合其他多個trait。
trait支持抽象方法,用于強制實體類實現trait的抽象方法
trait支持靜態方法
trait支持屬性,但注意沖突,一般是不能定義同樣名稱的屬性。我們建議避免trait和class出現相同名稱的屬性,即使訪問可見性、初始值都一樣(允許的),我也不建議,會導致混亂。
匿名類
php7開始支持匿名類,用于創建一次性簡單對象。
$util->setLogger(new class {
public function log($msg)
{
echo $msg;
}
});
//也就是不需要先定義一個logger的對象,再new給setLogger方法。
魔術方法
PHP所提供的重載(overloading)是指動態地創建類屬性和方法。我們是通過魔術方法(magic methods)來實現的。
PHP中的重載與其它絕大多數面向對象語言不同。傳統的重載是用于提供多個同名的類方法,但各方法的參數類型和個數不同。
clone
對象復制可以通過 clone 關鍵字來完成(如果可能,這將調用對象的 __clone() 方法)。
當對象被復制后,PHP 5 會對對象的所有屬性執行一個淺復制(shallow copy)。所有的引用屬性 仍然會是一個指向原來的變量的引用。如果有少量及簡單的引用屬性,可以在魔術方法__clone中手動實現引用屬性的復制。
class AA{
public $object1;
function __clone()
{
// 強制復制一份this->object, 否則仍然指向同一個對象
$this->object1 = clone $this->object1;
}
}
$a = new AA();
$a2 = clone $a;//通過__clone方法,達到對$a對象的深拷貝。
static::后期靜態綁定
self、parent、static
我們都知道parent的用法,就是從當前類(子類)往上追溯基類的存在的這個方法;static則是從當前類(父類、基類)往下追溯至當前執行實例(子類)中存在的方法;
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // 后期靜態綁定從這里開始,這里會執行調用test方法的實例的who方法,也就是B類實例的who方法。
self::who();//這里只會調用當前類中的who方法,如果有的話,也就是當前代碼行所在類A
parent::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();//輸出:BA
對象與引用及傳址賦值與引用賦值
在php5 的對象編程經常提到的一個關鍵點是“默認情況下對象是通過引用傳遞的”。但其實這不是完全正確的。
PHP 的引用是別名,就是兩個不同的變量名字指向相同的內容。
在 PHP 5以后,一個對象變量已經不再保存整個對象的值。而是保存一個標識符來訪問真正的對象內容。
當對象作為參數傳遞,作為結果返回,或者賦值給另外一個變量,另外一個變量跟原來的不是引用的關系,只是他們都保存著同一個標識符的拷貝,這個標識符指向同一個對象的真正內容。
這標識符我們可以理解為存儲地址,所以php的對象正常情況下的復制、傳遞都是通過傳址的方式,而不是引用,雖然效果是一樣的。
class A {
public $foo = 1;
}
$a = new A;
$b = $a; // $a ,$b都是同一個標識符的拷貝,意味著a、b指向同一個地址
// ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";//輸出:2
$c = new A;
$d = &$c; // $c ,$d是引用,d是c的一個別名,其實他們還是一樣的
// ($c,$d) = <id>
$d->foo = 2;
echo $c->foo."\n";//輸出:2
$e = new A;
function foo($obj) {//e和obj都是同一個標識符的拷貝
// ($obj) = ($e) = <id>
$obj->foo = 2;
}
foo($e);
echo $e->foo."\n";//輸出:2