一、PHP類和對象
類是面向?qū)ο蟪绦蛟O(shè)計的基本概念扰藕,通俗的理解類就是對現(xiàn)實中某一個種類的東西的抽象缓苛, 比如汽車可以抽象為一個類,汽車擁有名字邓深、輪胎未桥、速度捧灰、重量等屬性加矛,可以有換擋、前進冯勉、后退等操作方法萌壳。
通常定義一個汽車類的方法為:
class Car {
$name = '汽車';
function getName() {
return $this->name;
}
}
類是一類東西的結(jié)構(gòu)描述亦镶,而對象則是一類東西的一個具體實例日月,例如汽車這個名詞可以理解為汽車的總類,但這輛汽車則是一個具體的汽車對象缤骨。
對象通過new關(guān)鍵字進行實例化:
$car = new Car();
echo $car->getName();
類與對象看起來比較相似爱咬,但實際上有本質(zhì)的區(qū)別,類是抽象的概念绊起,對象是具體的實例精拟。類可以使程序具有可重用性。
1虱歪、創(chuàng)建一個對象
本節(jié)開始我們使用汽車舉例來認(rèn)識了類與對象蜂绎,本節(jié)我們來了解一下類的定義方法,類通過關(guān)鍵字class開頭笋鄙,然后是類名與花括號荡碾,在花括號中定義類的屬性與方法。類名必須是字母或下劃線開頭局装,后面緊跟若干個字母、數(shù)字或下劃線劳殖,類名最好能夠表意铐尚,可以采用名詞或者英文單詞。
//定義一個類
class Car {
//定義屬性
public $name = '汽車';
//定義方法
public function getName() {
//方法內(nèi)部可以使用$this偽變量調(diào)用對象的屬性或者方法
return $this->name;
}
}
要創(chuàng)建一個類的實例哆姻,可以使用new關(guān)鍵字創(chuàng)建一個對象宣增。
$car = new Car();
//也可以采用變量來創(chuàng)建
$className = 'Car';
$car = new $className();
2、類的屬性
在類中定義的變量稱之為屬性矛缨,通常屬性跟數(shù)據(jù)庫中的字段有一定的關(guān)聯(lián)爹脾,因此也可以稱作“字段”。屬性聲明是由關(guān)鍵字 public箕昭,protected 或者 private 開頭灵妨,后面跟一個普通的變量聲明來組成。屬性的變量可以設(shè)置初始化的默認(rèn)值落竹,默認(rèn)值必須是常量泌霍。
訪問控制的關(guān)鍵字代表的意義為:
public:公開的
protected:受保護的
private:私有的
class Car {
//定義公共屬性
public $name = '汽車';
//定義受保護的屬性
protected $corlor = '白色';
//定義私有屬性
private $price = '100000';
}
默認(rèn)都為public,外部可以訪問述召。一般通過->
對象操作符來訪問對象的屬性或者方法朱转,對于靜態(tài)屬性則使用::
雙冒號進行訪問。當(dāng)在類成員方法內(nèi)部調(diào)用的時候积暖,可以使用$this偽變量調(diào)用當(dāng)前對象的屬性藤为。
$car = new Car();
echo $car->name; //調(diào)用對象的屬性
echo $car->color; //錯誤 受保護的屬性不允許外部調(diào)用
echo $car->price; //錯誤 私有屬性不允許外部調(diào)用
受保護的屬性與私有屬性不允許外部調(diào)用,在類的成員方法內(nèi)部是可以調(diào)用的夺刑。
class Car{
private $price = '1000';
public function getPrice() {
return $this->price; //內(nèi)部訪問私有屬性
? }
}
3缅疟、定義類的方法
方法就是在類中的function分别,很多時候我們分不清方法與函數(shù)有什么差別,在面向過程的程序設(shè)計中function叫做函數(shù)窿吩,在面向?qū)ο笾衒unction則被稱之為方法茎杂。
同屬性一樣,類的方法也具有public纫雁,protected 以及 private 的訪問控制煌往。
訪問控制的關(guān)鍵字代表的意義為:
public:公開的
protected:受保護的
private:私有的
我們可以這樣定義方法:
class Car {
public function getName() {
return '汽車';
}
?}
$car = new Car();
echo $car->getName();
使用關(guān)鍵字static修飾的,稱之為靜態(tài)方法轧邪,靜態(tài)方法不需要實例化對象刽脖,可以通過類名直接調(diào)用,操作符為雙冒號::忌愚。
class Car {
public static function getName() {
return '汽車';
}
?}
echo Car::getName(); //結(jié)果為“汽車”
4曲管、定義類的方法PHP類和對象之構(gòu)造函數(shù)和析構(gòu)函數(shù)
PHP5可以在類中使用__construct()
定義一個構(gòu)造函數(shù),具有構(gòu)造函數(shù)的類硕糊,會在每次對象創(chuàng)建的時候調(diào)用該函數(shù)院水,因此常用來在對象創(chuàng)建的時候進行一些初始化工作。
class Car {
function __construct() {
print "構(gòu)造函數(shù)被調(diào)用\n";
}
}
$car = new Car(); //實例化的時候 會自動調(diào)用構(gòu)造函數(shù)__construct简十,這里會輸出一個字符串
在子類中如果定義了__construct
則不會調(diào)用父類的__construct
檬某,如果需要同時調(diào)用父類的構(gòu)造函數(shù),需要使用parent::__construct()
顯式的調(diào)用螟蝙。
class Car {
function __construct() {
print "父類構(gòu)造函數(shù)被調(diào)用\n";
}
}
class Truck extends Car {
function __construct() {
print "子類構(gòu)造函數(shù)被調(diào)用\n";
parent::__construct();
}
}
$car = new Truck();
同樣恢恼,PHP5支持析構(gòu)函數(shù),使用__destruct()
進行定義胰默,析構(gòu)函數(shù)指的是當(dāng)某個對象的所有引用被刪除场斑,或者對象被顯式的銷毀時會執(zhí)行的函數(shù)。
class Car {
function __construct() {
print "構(gòu)造函數(shù)被調(diào)用 \n";
}
function __destruct() {
print "析構(gòu)函數(shù)被調(diào)用 \n";
}
}
$car = new Car(); //實例化時會調(diào)用構(gòu)造函數(shù)
echo '使用后牵署,準(zhǔn)備銷毀car對象 \n';
unset($car); //銷毀時會調(diào)用析構(gòu)函數(shù)
當(dāng)PHP代碼執(zhí)行完畢以后漏隐,會自動回收與銷毀對象,因此一般情況下不需要顯式的去銷毀對象奴迅。
4锁保、PHP類和對象之Static靜態(tài)關(guān)鍵字
靜態(tài)屬性與方法可以在不實例化類的情況下調(diào)用,直接使用類名::方法名的方式進行調(diào)用半沽。靜態(tài)屬性不允許對象使用->
操作符調(diào)用爽柒。
class Car {
private static $speed = 10;
public static function getSpeed() {
return self::$speed;
}
}
echo Car::getSpeed(); //調(diào)用靜態(tài)方法
靜態(tài)方法也可以通過變量來進行動態(tài)調(diào)用
$func = 'getSpeed';
$className = 'Car';
echo $className::$func(); //動態(tài)調(diào)用靜態(tài)方法
靜態(tài)方法中,$this
偽變量不允許使用者填『拼澹可以使用self
,parent
占哟,static
在內(nèi)部調(diào)用靜態(tài)方法與屬性心墅。
class Car {
private static $speed = 10;
public static function getSpeed() {
return self::$speed;
}
public static function speedUp() {
return self::$speed+=10;
}
}
class BigCar extends Car {
public static function start() {
parent::speedUp();
}
}
BigCar::start();
echo BigCar::getSpeed();
5酿矢、PHP類和對象之訪問控制
前面的小節(jié),我們已經(jīng)接觸過訪問控制了怎燥,訪問控制通過關(guān)鍵字public
瘫筐,protected
和private
來實現(xiàn)。被定義為公有的類成員可以在任何地方被訪問铐姚。被定義為受保護的類成員則可以被其自身以及其子類和父類訪問策肝。被定義為私有的類成員則只能被其定義所在的類訪問。
類屬性必須定義為公有隐绵、受保護之众、私有之一。為兼容PHP5以前的版本依许,如果采用 var
定義棺禾,則被視為公有。
class Car {
$speed = 10; //錯誤 屬性必須定義訪問控制
public $name; //定義共有屬性
}
類中的方法可以被定義為公有峭跳、私有或受保護膘婶。如果沒有設(shè)置這些關(guān)鍵字,則該方法默認(rèn)為公有蛀醉。
class Car {
? //默認(rèn)為共有方法
function turnLeft() {
}
}
如果構(gòu)造函數(shù)定義成了私有方法竣付,則不允許直接實例化對象了,這時候一般通過靜態(tài)方法進行實例化滞欠,在設(shè)計模式中會經(jīng)常使用這樣的方法來控制對象的創(chuàng)建,比如單例模式只允許有一個全局唯一的對象肆良。
class Car {
private function __construct() {
echo 'object create';
}
private static $_object = null;
public static function getInstance() {
if (empty(self::$_object)) {
self::$_object = new Car(); //內(nèi)部方法可以調(diào)用私有方法筛璧,因此這里可以創(chuàng)建對象
}
return self::$_object;
}
}
//$car = new Car(); //這里不允許直接實例化對象
$car = Car::getInstance(); //通過靜態(tài)方法來獲得一個實例
6、PHP類和對象之對象繼承
繼承是面向?qū)ο蟪绦蛟O(shè)計中常用的一個特性惹恃,其實就是新的類包含舊的類中的屬性(和方法)夭谤,本文用生物舉例子。
生物是一個比較大的類巫糙,我們也可以稱之為基類朗儒,除此之外,以生物為父類的有細(xì)胞生物和非細(xì)胞生物参淹,非細(xì)胞生物其實就是病毒醉锄,繼承了細(xì)胞生物的類就是原核生物和真核生物,原核生物就是細(xì)菌浙值,繼承了真核生物的類有植物恳不、動物和真菌,繼承了植物的類有孢子植物和種子植物开呐,繼承了動物的類有脊椎動物和無脊椎動物烟勋,接下來還有別的類分別繼承這些類规求。
生物類中有很多屬性和方法,屬性有體積卵惦、質(zhì)量阻肿、壽命長短等,方法有自我復(fù)制(繁殖功能)等沮尿。繼承下來之后丛塌,即使脊椎動物沒有再編寫體積、質(zhì)量蛹找、壽命長短姨伤、自我復(fù)制等屬性和功能,該類也自動擁有以上屬性庸疾。這就實現(xiàn)了代碼的復(fù)用乍楚。
在子類中使用parent::父類方法名
可以調(diào)用父類方法。
7届慈、PHP類和對象之重載
由于PHP是弱類型語言徒溪,因此函數(shù)的輸入?yún)?shù)類型無法確定(可以使用類型暗示,但是類型暗示無法用在諸如整型金顿,字符串之類的標(biāo)量類型上)臊泌,并且對于一個函數(shù),比如只定義了3個輸入?yún)?shù)揍拆,PHP卻運行調(diào)用的時候輸入4個或者更多的參數(shù)渠概。因此基于這2點,注定了PHP中無法重載函數(shù)嫂拴,(類似Javascript語言)播揪,也無法有構(gòu)造函數(shù)的重載。
由于實現(xiàn)函數(shù)的重載對提高開發(fā)效率很有幫助筒狠,如果能象C#或者C++那樣猪狈,那就非常好了。事實上辩恼,PHP的提供了一個魔術(shù)方法雇庙,mixed __call ( string name, array arguments )
。這個方法在php手冊中也有提及灶伊,根據(jù)官方文檔疆前,稱此方法可以實現(xiàn)函數(shù)重載。當(dāng)調(diào)用對象中一個不存在的方法的時候聘萨,如果定義了__call()
方法峡继,則會調(diào)用該方法。比如下面的代碼:
<?php
class A
{
function __call ( $name, $arguments )
{
echo "__call調(diào)用<br/>";
echo '$name為'.$name."<br/>";
print_r ($arguments);
}
}
(new A)->test("test","argument");
?>
運行結(jié)果為:
__call
調(diào)用$name
為test
Array ( [0] => test [1] => argument )
因此只需要利用該魔術(shù)方法既可以實現(xiàn)函數(shù)重載匈挖。
代碼如下:
<?php
class A
{
function __call ($name, $args )
{
if($name=='f')
{
$i=count($args);
if (method_exists($this,$f='f'.$i)) {
call_user_func_array(array($this,$f),$args);
}
}
}
function f1($a1)
{
echo "1個參數(shù)".$a1."<br/>";
}
function f2($a1,$a2)
{
echo "2個參數(shù)".$a1.",".$a2."<br/>";
}
function f3($a1,$a2,$a3)
{
echo "3個參數(shù)".$a1.",".$a2.",".$a3."<br/>";
}
}
(new A)->f('a');
(new A)->f('a','b');
(new A)->f('a','b','c');
?>
輸出如下:
1個參數(shù)a
2個參數(shù)a,b
3個參數(shù)a,b,c
同樣的在PHP中碾牌,實現(xiàn)構(gòu)造函數(shù)的重載康愤,也只能變通的實現(xiàn)。實現(xiàn)的關(guān)鍵要素是獲取輸入?yún)?shù)舶吗,并且根據(jù)輸入?yún)?shù)判斷調(diào)用哪個方法征冷。下面是一個示例代碼:
<?php
class A
{
function __construct()
{
echo "執(zhí)行構(gòu)造函數(shù)<br/>";
$a = func_get_args(); //獲取構(gòu)造函數(shù)中的參數(shù)
$i = count($a);
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array(array($this,$f),$a);
}
}
function __construct1($a1)
{
echo "1個參數(shù)".$a1."<br/>";
}
function __construct2($a1,$a2)
{
echo "2個參數(shù)".$a1.",".$a2."<br/>";
}
function __construct3($a1,$a2,$a3)
{
echo "3個參數(shù)".$a1.",".$a2.",".$a3."<br/>";
}
}
$o = new A('a');
$o = new A('a','b');
$o = new A('a','b','c');
?>
執(zhí)行構(gòu)造函數(shù)
1個參數(shù)a
執(zhí)行構(gòu)造函數(shù)
2個參數(shù)a,b
執(zhí)行構(gòu)造函數(shù)
3個參數(shù)a,b,c
順便提一下,和c#等面向?qū)ο笳Z言一樣誓琼,php中检激,把構(gòu)造函數(shù)設(shè)成私有或者protected,就不能實例化該對象了腹侣。
PHP中的重載指的是動態(tài)的創(chuàng)建屬性與方法叔收,是通過魔術(shù)方法來實現(xiàn)的。屬性的重載通過__set
傲隶,__get
饺律,__isset
,__unset
來分別實現(xiàn)對不存在屬性的賦值跺株、讀取复濒、判斷屬性是否設(shè)置、銷毀屬性乒省。
class Car {
private $ary = array();
public function __set($key, $val) {
$this->ary[$key] = $val;
}
public function __get($key) {
if (isset($this->ary[$key])) {
return $this->ary[$key];
}
return null;
}
public function __isset($key) {
if (isset($this->ary[$key])) {
return true;
}
return false;
}
public function __unset($key) {
unset($this->ary[$key]);
}
}
$car = new Car();
$car->name = '汽車'; //name屬性動態(tài)創(chuàng)建并賦值
echo $car->name;
方法的重載通過__call
來實現(xiàn)巧颈,當(dāng)調(diào)用不存在的方法的時候,將會轉(zhuǎn)為參數(shù)調(diào)用__call
方法袖扛,當(dāng)調(diào)用不存在的靜態(tài)方法時會使用__callStatic
重載砸泛。
class Car {
public $speed = 0;
public function __call($name, $args) {
if ($name == 'speedUp') {
$this->speed += 10;
}
}
}
$car = new Car();
$car->speedUp(); //調(diào)用不存在的方法會使用重載
echo $car->speed;
8、PHP類和對象之對象的高級特性
(1)對象比較
當(dāng)同一個類的兩個實例的所有屬性都相等時蛆封,可以使用比較運算符==
進行判斷唇礁,當(dāng)需要判斷兩個變量是否為同一個對象的引用時,可以使用全等運算符===
進行判斷娶吞。
class Car {
}
$a = new Car();
$b = new Car();
if ($a == $b) echo '=='; //true
if ($a === $b) echo '==='; //false
(2)對象復(fù)制
在一些特殊情況下,可以通過關(guān)鍵字clone來復(fù)制一個對象械姻,這時__clone
方法會被調(diào)用妒蛇,通過這個魔術(shù)方法來設(shè)置屬性的值。
class Car {
public $name = 'car';
public function __clone() {
$obj = new Car();
$obj->name = $this->name;
}
}
$a = new Car();
$a->name = 'new car';
$b = clone $a;
var_dump($b);
(3)對象序列化
可以通過serialize
方法將對象序列化為字符串楷拳,用于存儲或者傳遞數(shù)據(jù)绣夺,然后在需要的時候通過unserialize
將字符串反序列化成對象進行使用。
class Car {
public $name = 'car';
}
$a = new Car();
$str = serialize($a); //對象序列化成字符串
echo $str.'<br>';
$b = unserialize($str); //反序列化為對象
var_dump($b);
二欢揖、異常
從PHP5開始陶耍,PHP支持異常處理,異常處理是面向?qū)ο笠粋€重要特性她混,PHP代碼中的異常通過throw拋出烈钞,異常拋出之后泊碑,后面的代碼將不會再被執(zhí)行。
既然拋出異常會中斷程序執(zhí)行毯欣,那么為什么還需要使用異常處理馒过?
異常拋出被用于在遇到未知錯誤,或者不符合預(yù)先設(shè)定的條件時酗钞,通知客戶程序腹忽,以便進行其他相關(guān)處理,不至于使程序直接報錯中斷砚作。
當(dāng)代碼中使用了try catch的時候窘奏,拋出的異常會在catch中捕獲,否則會直接中斷
1葫录、語法
try{
//可能出現(xiàn)錯誤或異常的代碼
//catch表示捕獲着裹,Exception是php已定義好的異常類
} catch(Exception $e){
//對異常處理,方法:
//1压昼、自己處理
//2求冷、不處理,將其再次拋出
}
處理處理程序應(yīng)當(dāng)包括:
Try:使用異常的函數(shù)應(yīng)該位于 "try" 代碼塊內(nèi)窍霞。如果沒有觸發(fā)異常匠题,則代碼將照常繼續(xù)執(zhí)行。但是如果異常被觸發(fā)但金,會拋出一個異常韭山。
Throw:這里規(guī)定如何觸發(fā)異常。注意:每一個 "throw" 必須對應(yīng)至少一個 "catch"冷溃,當(dāng)然可以對應(yīng)多個"catch"
Catch - "catch" 代碼塊會捕獲異常钱磅,并創(chuàng)建一個包含異常信息的對象。
拋異常演示:
//創(chuàng)建可拋出一個異常的函數(shù)
function checkNum($number){
if($number>1){
throw new Exception("異常提示-數(shù)字必須小于等于1");
}
return true;
}
//在 "try" 代碼塊中觸發(fā)異常
try{
checkNum(2);
//如果異常被拋出似枕,那么下面一行代碼將不會被輸出
echo '如果能看到這個提示盖淡,說明你的數(shù)字小于等于1';
}catch(Exception $e){
//捕獲異常
echo '捕獲異常: ' .$e->getMessage();
}
上面代碼將獲得類似這樣一個錯誤:
捕獲異常:: 異常提示-數(shù)字必須小于等于1
演示解釋:
上面的代碼拋出了一個異常,并捕獲了它:
創(chuàng)建 checkNum() 函數(shù)凿歼。它檢測數(shù)字是否大于 1褪迟。如果是,則拋出一個異常答憔。
在 "try" 代碼塊中調(diào)用 checkNum() 函數(shù)味赃。
checkNum() 函數(shù)中的異常被拋出
"catch" 代碼塊接收到該異常,并創(chuàng)建一個包含異常信息的對象 $e
虐拓。
通過從這個 exception 對象調(diào)用 $e->getMessage()
心俗,輸出來自該異常的錯誤消息
2、異常處理類
PHP具有很多異常處理類,其中Exception是所有異常處理的基類城榛。
Exception具有幾個基本屬性與方法揪利,其中包括了:
message 異常消息內(nèi)容
code 異常代碼
file 拋出異常的文件名
line 拋出異常在該文件的行數(shù)
其中常用的方法有:
getTrace 獲取異常追蹤信息
getTraceAsString 獲取異常追蹤信息的字符串
getMessage 獲取出錯信息
如果必要的話,可以通過繼承Exception類來建立自定義的異常處理類吠谢。
//自定義的異常類土童,繼承了PHP的異常基類Exception
class MyException extends Exception {
function getInfo() {
return '自定義錯誤信息';
}
}
try {
//使用異常的函數(shù)應(yīng)該位于 "try" 代碼塊內(nèi)工坊。如果沒有觸發(fā)異常献汗,則代碼將照常繼續(xù)執(zhí)行。但是如果異常被觸發(fā)王污,會拋出一個異常罢吃。
throw new MyException('error');//這里規(guī)定如何觸發(fā)異常。注意:每一個 "throw" 必須對應(yīng)至少一個 "catch"昭齐,當(dāng)然可以對應(yīng)多個"catch"
} catch(Exception $e) {//"catch" 代碼塊會捕獲異常尿招,并創(chuàng)建一個包含異常信息的對象
echo $e->getInfo();//獲取自定義的異常信息
echo $e->getMessage();//獲取繼承自基類的getMessage信息
}
3、捕獲異常信息
在了解了異常處理的基本原理之后阱驾,我們可以通過try catch來捕獲異常就谜,我們將執(zhí)行的代碼放在try代碼塊中,一旦其中的代碼拋出異常里覆,就能在catch中捕獲丧荐。
這里我們只是通過案例來了解try catch的機制以及異常捕獲的方法,在實際應(yīng)用中喧枷,不會輕易的拋出異常虹统,只有在極端情況或者非常重要的情況下,才會拋出異常隧甚,拋出異常车荔,可以保障程序的正確性與安全,避免導(dǎo)致不可預(yù)知的bug戚扳。
一般的異常處理流程代碼為:
try {
throw new Exception('wrong');
} catch(Exception $ex) {
echo 'Error:'.$ex->getMessage().'<br>';
echo $ex->getTraceAsString().'<br>';
}
echo '異常處理后忧便,繼續(xù)執(zhí)行其他代碼';
4、獲取錯誤行數(shù)等信息
在異常被捕獲之后帽借,我們可以通過異常處理對象獲取其中的異常信息珠增,前面我們已經(jīng)了解捕獲方式,以及獲取基本的錯誤信息宜雀。
在實際應(yīng)用中切平,我們通常會獲取足夠多的異常信息握础,然后寫入到錯誤日志中辐董。
通過我們需要將報錯的文件名、行號禀综、錯誤信息简烘、異常追蹤信息等記錄到日志中苔严,以便調(diào)試與修復(fù)問題。
例如:
<?php
try {
throw new Exception('wrong');
} catch(Exception $ex) {
$msg = 'Error:'.$ex->getMessage()."\n";
$msg.= $ex->getTraceAsString()."\n";
$msg.= '異常行號:'.$ex->getLine()."\n";
$msg.= '所在文件:'.$ex->getFile()."\n";
//將異常信息記錄到日志中
PHP異常處理之 file_put_contents('error.log', $msg);
}