每一個類的定義都是關鍵class開頭。php
<?php namespace NS { class A { // 成員屬性 public $temp = '成員屬性'; // 方法 public function displayVar() { // 使用僞變量$this調用成員屬性 echo $this->temp . "<br>"; } } // 使用new關鍵字實例化一個類 $temp = new A(); echo $temp->displayVar(); class B extends A { // 構造方法 function __construct() { $this->temp = '成員屬性B繼承於A'; } } $temp = new B(); echo $temp->displayVar(); echo A::class; } ?>
public(公有),protected(受保護)或 private(私有)。python
能夠用 ->(對象運算符):$this->property(其中 property 是該屬性名)這種方式來訪問非靜態屬性。 靜態屬性則是用 ::(雙冒號):self::$property 來訪問。laravel
<?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 起 ?>
spl_autoload_register() 函數能夠註冊任意數量的自動加載器,當使用還沒有被定義的類(class)和接口(interface)時自動去加載。git
本例嘗試分別從 MyClass1.php 和 MyClass2.php 文件中加載 MyClass1 和 MyClass2 類。web
<?php spl_autoload_register(function ($class_name) { require_once $class_name . '.php'; }); $obj = new MyClass1(); $obj2 = new MyClass2(); ?>
<?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(); ?>
<?php class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } $obj = new MyDestructableClass(); ?>
因爲靜態方法不須要經過對象便可調用,因此僞變量 $this 在靜態方法中不可用。面試
靜態屬性不能夠由對象經過 -> 操做符來訪問。redis
用靜態方式調用一個非靜態方法會致使一個 E_STRICT 級別的錯誤。docker
任何一個類,若是它裏面至少有一個方法是被聲明爲抽象的,那麼這個類就必須被聲明爲抽象的。數據庫
<?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"; ?>
使用接口(interface),能夠指定某個類必須實現哪些方法,但不須要定義這些方法的具體內容。編程
接口是經過 interface 關鍵字來定義的,就像定義一個標準的類同樣,但其中定義全部的方法都是空的。
接口中定義的全部方法都必須是公有,這是接口的特性。
要實現一個接口,使用 implements 操做符。類中必須實現接口中定義的全部方法,不然會報一個致命錯誤。 類能夠實現多個接口,用逗號來分隔多個接口的名稱。
<?php class A { public function sayHello() { echo "Hello "; } } trait mytrait { public function traitFunction() { parent::sayHello(); echo "world!"; } } class B extends A { use mytrait; } $temp = new B(); $temp->traitFunction();
PHP 7 開始支持匿名類。
<?php // PHP 7 以前的代碼 class Logger { public function log($msg) { echo $msg; } } $util->setLogger(new Logger()); // 使用了 PHP 7+ 後的代碼 $util->setLogger(new class { public function log($msg) { echo $msg; } });
public void __set ( string $name , mixed $value )
public mixed __get ( string $name )
public bool __isset ( string $name )
public void __unset ( string $name )
在給不可訪問屬性賦值時,__set() 會被調用。
讀取不可訪問屬性的值時,__get() 會被調用。
當對不可訪問屬性調用 isset() 或 empty() 時,__isset() 會被調用。
當對不可訪問屬性調用 unset() 時,__unset() 會被調用。
參數 $name 是指要操做的變量名稱。__set() 方法的 $value 參數指定了 $name 變量的值。
屬性重載只能在對象中進行。在靜態方法中,這些魔術方法將不會被調用。 因此這些方法都不能被 聲明爲 static。從 PHP 5.3.0 起, 將這些魔術方法定義爲 static 會產生一個警告。
PHP 5 提供了一種定義對象的方法使其能夠經過單元列表來遍歷, 例如用 foreach 語句。默認狀況下,全部可見屬性都將被用於遍歷。
<?php class MyClass { public $var1 = 'value 1'; public $var2 = 'value 2'; public $var3 = 'value 3'; protected $protected = 'protected var'; private $private = 'private var'; function iterateVisible() { echo "MyClass::iterateVisible:<br>"; foreach ($this as $key => $value) { print '$key=>' . $key . "<br>"; print '$value=>' . $value . "<br>"; } } } $class = new MyClass(); $class->iterateVisible(); foreach ($class as $key => $value) { print '$key=>' . $key . "<br>"; print '$value=>' . $value . "<br>"; }
PHP 5 新增了一個 final 關鍵字。若是父類中的方法被聲明爲 final,則子類沒法覆蓋該方法。 若是一個類被聲明爲 final,則不能被繼承。
<?php class BaseClass { public function display() { echo 'final'; } final public function test() { echo 'Hello World!'; } } class A extends BaseClass { public function display() { echo 'A'; } public function test() { echo 'haha'; } } $temp = new A(); $temp->test(); #會報錯,final方法不能重寫
在多數狀況下,咱們並不須要徹底複製一個對象來得到其中屬性。但有一個狀況下確實須要:若是你有一個 GTK 窗口對象,該對象持有窗口相關的資源。
你可能會想複製一個新的窗口,保持全部屬性與原來的窗口相同,但必須是一個新的對象(由於若是不是新的對象,那麼一個窗口中的改變就會影響到另外一個窗口)。
還有一種狀況:
若是對象 A 中保存着對象 B 的引用,當你複製對象 A 時,你想其中使用的對象再也不是對象 B 而是 B 的一個副本,那麼你必須獲得對象 A 的一個副本。
當使用比較運算符(==)比較兩個對象變量時,比較的原則是:
若是兩個對象的屬性和屬性值 都相等,並且兩個對象是同一個類的實例,那麼這兩個對象變量相等。
而若是使用全等運算符(===),這兩個對象變量必定要指向某個類的同一個實例(即同一個對象)。
PHP 5 可使用類型約束。
若是一個類或接口指定了類型約束,則其全部的子類或實現也都如此。
類型約束不能用於標量類型如 int 或 string。Traits 也不容許。
<?php class MyClass { public function test_class(OtherClass $otherclass) { echo $otherclass->var; echo "<br>"; } public function test_array(array $input_array) { print_r($input_array); } } class OtherClass { public $var = 'Hello World!'; } $myclass = new MyClass(); $otherclass = new OtherClass(); $myclass->test_class($otherclass); $input_array = ['one' => 'first', 'two' => 'second']; $myclass->test_array($input_array);
自 PHP 5.3.0 起,PHP 增長了一個叫作後期靜態綁定的功能,用於在繼承範圍內引用靜態調用的類。
使用 self:: 或者 CLASS 對當前類的靜態引用,取決於定義當前方法所在的類:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { // self::who(); // 輸出A static::who(); // 輸出B } } class B extends A { public static function who() { echo __CLASS__; } } $temp = new B(); $temp::test();
在php5 的對象編程常常提到的一個關鍵點是「默認狀況下對象是經過引用傳遞的」。
但其實這不是徹底正確的。下面經過一些例子來講明。
PHP 的引用是別名,就是兩個不一樣的變量名字指向相同的內容。 在 PHP 5,一個對象變量已經再也不保存整個對象的值。只是保存一個標識符來訪問真正的對象內容。 當對象做爲參數傳遞,做爲結果返回,或者賦值給另一個變量,另一個變量跟原來的不是引用的關係, 只是他們都保存着同一個標識符的拷貝,這個標識符指向同一個對象的真正內容。
<?php class A { public $foo = 1; } $a = new A(); $b = $a; $b->foo = 2; echo $a->foo . "\n"; // 輸出爲2
序列化對象 - 在會話中存放對象
全部php裏面的值均可以使用函數serialize()來返回一個包含字節流的字符串來表示。 unserialize()函數可以從新把字符串變回php原來的值。 序列化一個對象將會保存對象的全部變量,可是不會保存對象的方法,只會保存類的名字。
<?php // //include("class.inc"); // //$a = new A(); //$a->show_one(); // //$s = serialize($a); //file_put_contents('store', $s); // 反序列化必需要包含此文件。 include("class.inc"); $s = file_get_contents('store'); $a = unserialize($s); $a->show_one();
<?php namespace my\name; class MyClass{} function myfunction(){} const MYCONST = 1; $a = new MyClass(); $c = new \my\name\MyClass; $a = strlen('hi'); $d = namespace\MYCONST; $d = __NAMESPACE__ . '\MYCONST'; echo $d; echo constant($d);
雖然任意合法的PHP代碼均可以包含在命名空間中,但只有如下類型的代碼受命名空間的影響,它們是:
類、接口、函數和常量。
命名空間經過關鍵字namespace來聲明。若是一個文件中包含命名空間,它必須在其它代碼以前聲明命名空間, 有一個除外:declare關鍵字。
<?php namespace space\MyProject; const CONNECT_OK = 1; class Connection{} function connect(){} echo __NAMESPACE__;
在同一個文件中定義多個命名空間有兩種語法形式。
不建議使用這種語法在單個文件中定義多個命名空間,區間會顯得不明確。
<?php namespace MyProject; const CONNECT_OK = 1; class Connection { } function connect() { } echo __NAMESPACE__ . "<br>"; namespace AnotherProject; const CONNECT_OK = 1; class Connection { } function connect() { } echo __NAMESPACE__ . "<br>"; // 不建議使用這種語法在單個文件中定義多個命名空間,區間會顯得不明確。
<?php namespace MyProject { const CONNECT_OK = 1; class Connection { } function connect() { } echo __NAMESPACE__ . "<br>"; } namespace AnotherProject { const CONNECT_OK = 1; class Connection { } function connect() { } echo __NAMESPACE__ . "<br>"; }
容許經過別名引用或導入外部的徹底限定名稱,是命名空間的一個重要特徵。
<?php namespace MyProject { const CONNECT_OK = 1; class Connection { public function __construct() { echo "MyProject __construct" . "<br>"; } } function connect() { echo "MyProject connect" . "<br>"; } echo __NAMESPACE__ . "<br>"; } namespace AnotherProject { const CONNECT_OK = 1; class Connection { public function __construct() { echo "AnotherProject __construct" . "<br>"; } } function connect() { echo "AnotherProject connect" . "<br>"; } $temp = new \MyProject\Connection(); use \MyProject as A; $temp = new A\Connection(); use \Myproject\Connection as AConnection; $temp = new AConnection(); echo __NAMESPACE__ . "<br>"; }
若是沒有定義任何命名空間,全部的類與函數的定義都是在全局空間,與PHP引入命名空間概念前同樣。
在名稱前加上前綴"\"表示該名稱是全局空間中的名稱,即便該名稱位於其它的命名空間中時也是如此。
<?php namespace www\web; class MyClass { public function display() { try { echo 'Hello World!'; } catch (\Exception $e) { echo 'try...catch'; } } } $temp = new MyClass(); $temp->display();
在一個命名空間中,當PHP遇到一個非限定的類、函數或常量名稱時,它使用不一樣的優先策略來解析該名稱。
類名稱老是解析到當前命名空間中的名稱。所以在訪問系統內部或不包含在命名空間中的類名稱時,必須使用徹底限定名稱。
對於函數和常量來講,若是當前命名空間中不存在該函數或常量,PHP會退而使用全局空間中的函數或常量。
<?php namespace A\B\C; class Exception extends \Exception { } $a = new Exception('message'); $b = new \Exception('message'); echo strlen('Hello World!'); // 正常輸出12 $c = new ArrayObject(); // 致命錯誤,找不到A\B\C\ArrayObject類
名稱中不包含命名空間分隔符的標識符,例如Foo
名稱中含有命名空間分隔符的標識符,例如Foo\Bar
名稱中包含命名空間分隔符,並以命名空間分隔符開始的標識符,例如\Foo\Bar。
生成器提供了一種更容易的方法來實現簡單的對象迭代,相比較定義類實現Iterator接口的方式,性能開銷和複雜性大大下降。
生成器容許你在foreach代碼塊中寫代碼來迭代一組數據而不須要在內存中建立一個數組,在內存中建立一個大數組可能會使你的內存達 到上限,或者會佔據可觀的處理時間。
生成器函數,就像一個普通的自定義函數同樣,和普通函數只返回一次不一樣的是,生成器能夠根據須要yield屢次,以便生成須要迭代的值。
一個簡單的例子就是使用生成器來從新實現 range() 函數。 標準的 range() 函數須要在內存中生成一個數組包含每個在它範圍內的值, 而後返回該數組, 結果就是會產生多個很大的數組。 好比,調用 range(0, 1000000) 將致使內存佔用超過 100 MB。
<?php function xrange($start, $end, $step = 1) { for ($i = $start; $i <= $end; $i += $step) { yield $i; } } // 直接報500錯誤 foreach (range(0, 1000000) as $val) { echo $val; } // 進入大循環 foreach (xrange(0, 1000000) as $val) { echo $val; }
個人理解就是鏈表來的,一個指針像遊標同樣,循環迭代。
一個生成器函數看起來像一個普通的函數,不一樣的是普通函數返回一個值,而一個生成器能夠yield生成許多它所須要的值。
當一個生成器被調用的時候,它返回一個能夠被遍歷的對象。當你遍歷這個對象的時候PHP將會在每次須要值的時候調用生成器函數, 並在產生一個值以後保存生成器的狀態,這樣它就能夠在須要產生下一個值的時候恢復調用狀態。
生成器函數的核心就是yield關鍵字。它最簡單的調用形式看起來像一個return申明,不一樣之處在於普通return會返回值並 終止函數的執行,而yield會返回一個值給循環調用今生成器的代碼而且只是暫停執行生成器函數。
<?php function test() { for ($i = 0; $i <= 10; $i++) { // 注意:變量$i的值在不一樣的yield之間是保持傳遞的。 yield $i; } } $temp = test(); foreach ($temp as $value) { echo "$value" . "<br>"; }
<?php $input = <<<'EOF' 1;PHP;Likes dollar signs 2;Python;Likes whitespace 3;Ruby;Likes blocks EOF; function input_parse($input) { foreach (explode("\n", $input) as $line) { $fields = explode(';', $line); $id = array_shift($fields); // array_shift將數組開頭的單元移出數組 yield $id => $fields; } } foreach (input_parse($input) as $key => $value) { echo '$key=>' . $key . "<br>"; echo '$value=>' . print_r($value) . "<br>"; }
yield能夠在沒有參數傳入的狀況下被調用來生成一個NULL值並配對一個自動的鍵名。
可使用生成器來初始化一個null數組。
<?php function test() { foreach (range(1, 3) as $i) { yield; } } var_dump(iterator_to_array(test()));
生成函數能夠像使用值同樣來使用引用生成。
<?php function &test() { $value = 10; while ($value > 0) { yield $value; } } foreach (test() as &$value) { echo (--$value) . '...'; }
在PHP中引用意味着用不一樣的名字訪問同一個變量內容。這並不像C的指針: 例如你不能對他們作指針運算,他們並非實際的內存地址。
在PHP中,變量名和變量的內容是不同的,所以一樣的內容能夠有不一樣的名字。
$a = 'Hello world!'; $b = &$a; $b = 'new Hello world!'; //echo $a; function test(&$temp){ $temp = 'function Hello world!'; } test($b); echo $a;
引用返回用在當想用函數找到引用應該被綁定在哪個變量上面時。
不要用返回引用來增長性能,引擎足夠聰明來本身進行優化。
僅在有合理的技術緣由時才返回引用!要返回引用,請看以下示例:
class temp { public $value = 100; public function &getValue() { return $this->value; } } $obj = new temp(); $value = &$obj->getValue(); echo $value . '<br>'; // 輸出100 $obj->value = 9; echo $value . '<br>'; // 輸出9
當unset一個引用,只是斷開了變量名和變量內容之間的綁定。這並不意味着變量內容被銷燬了。
$a = 1; $b = &$a; unset($a);
許多PHP的語法結構 是經過引用機制實現的,因此上述有關引用綁定的一切也都適用於這些結構。
當用global $var聲明一個變量時實際上創建了一個到全局變量的引用。
global $var; $var = & $GLOBALS['var']; // 與上一行代碼是等價的。
在一個對象的方法中,$this永遠是調用它的對象的引用。
class myclass{ //成員變量(公有訪問控制) public $myvar; //構造函數 function __construct($myvar){ $this->myvar = $myvar; } //封裝 function setValue($myvar){ $this->myvar = $myvar; } function getValue(){ return $this->myvar; } //析構函數 function __destruct(){ unset($this->myvar); } } //類繼承 class myextends extends myclass{ } $myclass = new myextends("Moments"); echo $myclass->getValue();
class myclass{ //使用靜態變量 public static $myvar = "Moments"; public function myfun(){ return self::$myvar; } } $myclass = new myclass(); echo myclass::$myvar; echo $myclass->myfun();
abstract class myclass{ public $myvar; //強制要求子類定義之些方法 abstract protected function setValue($myvar); //普通方法 public function getValue(){ return $this->myvar; } } class myextends extends myclass{ function setValue($myvar){ $this->myvar = $myvar; } function getValue(){ return parent::getValue(); } } $myclass = new myextends(); $myclass->setValue("Moments"); echo $myclass->getValue();
#接口中定義的全部方法都必須是公有 interface myinterface{ //接口不支持成員變量,可使用常量 const myconst = "myconst"; //類中必須實現接口中定義的全部方法。 public function setValue($myvar); public function getValue(); } class myextends implements myinterface{ public function setValue($myvar){ $this->myvar = $myvar; } public function getValue(){ return $this->myvar; } } $myclass = new myextends(); $myclass->setValue("Moments"); echo $myclass->getValue(); echo myinterface::myconst;