php核心技術與最佳實踐--- oop

<?php
/**
 * Created by PhpStorm.
 * User: cl
 * Date: 2019/8/12
 * Time: 7:08
 */
/*oop*/
class Person{
    public $name;
    public $gender;
    public function say(){
        echo $this->name,'is',$this->gender;
    }
}

$student = new Person();
$student->name = "CL";
$student->gender = "MAN";
$student->say();
// CLisMAN
var_dump((array)$student);
// array(2) { ["name"]=> string(2) "CL" ["gender"]=> string(3) "MAN" }
// 對象由屬性組成,一個對象的屬性是它區別於另外一個對象的關鍵所在。因爲php的對象使用數組來模擬的
// 所以咱們把對象轉成數組,就能看見這個對象所擁有的屬性了。
// 到這裏,能夠直觀的認識到,對象就是一堆數據。既然如此,能夠把一個對象存儲起來,以便須要時用,這就是對象的序列化。
$str = serialize($student);
var_dump($str);
// string(60) "O:6:"Person":2:{s:4:"name";s:2:"CL";s:6:"gender";s:3:"MAN";}"
// 在須要時,能夠反序列化取出這個對象
var_dump(unserialize($str));
// object(Person)#2 (2) { ["name"]=> string(2) "CL" ["gender"]=> string(3) "MAN" }
// 能夠看到,對象序列化後,存儲的只是對象的屬性。類是由屬性和方法組成的,而對象則是屬性的集合,由同一個類生成的不一樣對象,
// 擁有各自不一樣的屬性,但共享了類的代碼空間中方法區域的代碼。

/*對象與數組*/
// 數組是由鍵值對數據組成,數組的鍵值對和對象的屬性/屬性值對十分類似。對象序列化後和數組序列化後的結果是
// 驚人的類似
$student_arr = ['name' => 'CL' , 'gender' => 'MAN'];
var_dump(serialize($student_arr));
//  string(49) "a:2:{s:4:"name";s:2:"CL";s:6:"gender";s:3:"MAN";}"
// 區別在於 : 對象中還包含指針,指向了它所屬的類。

/* 對象與類*/
// 若是對象中還包含對象,那麼序列化後會是什麼樣子呢?
class Family{
    public $people;
    public $location;
    public function __construct ($p,$loc){
        $this->people = $p;
        $this->location = $loc;
    }
/*    public function __destruct(){
        var_dump('魔術方法之析構方法');
    }*/
}
$tom = new Family($student,"peking");
var_dump(serialize($tom));
// string(118) "O:6:"Family":2:{s:6:"people";O:6:"Person":2:{s:4:"name";s:2:"CL";s:6:"gender";s:3:"MAN";}s:8:"location";s:6:"peking";}"
/* 能夠看出,序列化後的對象會附帶所屬類型,這個類名保證此對象在執行類的方法(也是本身所能執行的方法)時,
    可以正確地找到方法所在的代碼空間(即對象所擁有的方法存儲在類裏)。另外,當一個對象的實例變量引用其餘對象時,序列化對象
    也會對引用對象進行序列化
    由此能夠分析兩者的關係 :
    類是定義一系列屬性和操做的模板,而對象則是把屬性進行具體化,而後交給類處理
    對象就是數據,對象自己包含方法。可是對象有一個」指針「指向一個類,這個類裏能夠有方法。
    方法描述不一樣屬性致使的不一樣表現
    類和對象是不可分割的,有對象就一定有一個類與其對應,不然這個對象也就成了沒有親人的孩子(但有一個特殊的狀況,就是
    由標量進行強制類型轉換的object,沒有一個類與它對應。此時,PHP中一個稱爲」孤兒「的stdClass類就會收留這個對象)
    能夠看出,在面向對象層面,js和php區別仍是很大的。
*/

/*魔術方法*/
/*
 * 魔術方法是以兩個下劃線開頭,具備特殊做用的一些方法,能夠看做是PHP的語法糖
 * 語法糖是指那些沒有給計算機添加新功能,而對人類來講更甜蜜的語法
 * Family類的__construct方法就是一個標準的魔術方法。這個魔術方法又稱構造方法。具備構造方法的類會每次建立對象時
 * 先調用此方法,因此很是適合在使用對象以前作一些初始化工做,如:給屬性賦值,鏈接數據庫等
 * 有構造方法就有析構方法,即destruct方法,這個方法會在某個對象的因此引用都被刪除,或者當對象被顯式銷燬時執行。
 * 這兩個方法是最多見也是最有用的魔術方法
 */
/*魔術方法之set 和 get*/
class Account{
    private $user = 1;
    private $pwd = 2;

    public function __get($name){
        echo '請求'.$name;
    }
    public function __set($name,$val){
        echo "請求設置".$name."爲".$val;
    }


    public function __call($name ,$arguments){
        // $name : 要調用的方法名稱
        // $arguments : 數組,包含着要傳遞給方法的參數
        var_dump($name);
        var_dump($arguments);
    }
    public static function __callStatic($name ,$arguments){
        var_dump($name);
        var_dump($arguments);
    }
    public function __toString()
    {
        return '任意字符串';
    }

}
$a = new Account();
$a->user; // 若是沒有 __get 魔術方法則會報錯,大體意思是不能訪問對象的私有屬性
// 請求user
$a->name = '123'; // 由於對象沒有name屬性,因此會觸發 __set 魔術方法
// 請求設置name爲123
/*
 * 能夠直觀的看到,若類中定義了set 和 get這一對魔術方法,那麼當給對象屬性賦值或取值時,即便
 * 這個屬性不存在,也不會報錯,必定程度上加強了程序的健壯性
 */
// 那麼,若是防止調用一個不可訪問的方法(如未定義,或者不可見)時,call()會被調用。
$a->demo('1','2');
// "demo"
// array(2) { [0]=> string(1) "1" [1]=> string(1) "2" }
// 當調用的靜態方法不存在或權限不足時,callStatic() 會被調用
Account::demo();
// "demo"
// array(0) { }

// 固然,使用魔術方法"防止調用不存在的方法而報錯",並非魔術方法的本意。實際上,魔術方法使方法的動態建立變爲可能。
// 這在MVC等框架設計中是頗有用的語法。能夠經過一段代碼使用callStatic這一魔術方法進行方法的動態建立和延遲綁定

// toString方法
echo $a; //任意字符串
// 好比打印一個對象時,看看這個對象都有哪些屬性,其值是什麼,若是定義了toString方法,就能在測試時,echo打印對象體
// 對象會自動調用它所屬類定義的toString方法,格式化輸出這個對象所包含的數據。若是沒有這個方法,那麼echo一個對對象將報錯

 

 

<?php
/**
 * Created by PhpStorm.
 * User: chenglin
 * Date: 2019/8/12
 * Time: 7:08
 */
/*oop高級*/
class Person{
    public $name = 'tom';
    public $gender;
    public $money = 1000;
    public function  __construct(){
        echo '這裏是父類';

    }
    public function say(){
        var_dump( $this->name);
    }
}
class Family extends Person{
    public $name;
    public $gender;
    public $money = 100000;
    public function __construct(){
        parent::__construct();
        echo "這裏是子類";
    }
    public function say(){
        parent::say();
    }
}

$poor = new Family();
$poor->say();
// 這裏是父類
// 這裏是子類
// NULL

/*
 * 從上面的代碼中能夠了解繼承的實現。在繼承中,用parent指代父類,用self指代自身。
 * 使用"::"操做符(範圍解析操做符)調用父類的方法。"::"操做符還能夠做爲類常量和靜態方法的調用,不要把這兩種應用混淆。
 * 既然提到靜態,就再強調一點。若是聲明類成員或方法爲static,就能夠不實例化類而直接訪問
 */
/*接口*/
/*
 * 這裏,首先強調一個概念,面向接口編程並非一種新的編程範式。本章開頭提到的三大範式中並無提到面向接口。其實,
 * 這裏是狹義的接口,即 interface 關鍵字。廣義的接口能夠是任何一個對外提供服務的出口,好比提供數據傳輸的usb接口,
 * 、淘寶網對其餘網站開放的支付寶接口。
 * 接口定義一套規範,描述一個"物"的功能,要求若是現實中的"物"想成爲可用,就必須實現這些基本功能。接口這樣描述本身:
 *  "對於實現個人因此類,看起來都應該向我如今這個樣子"
 * 在程序中,接口的方法必須被所有實現,不然會報fetal錯誤
 */
interface mobile{
    public function run(); // 驅動方法
}
class plain implements mobile{
    public function run()
    {
        // TODO: Implement run() method.
        echo "我是飛機";
    }
    public function fly(){
        echo "飛行";
    }
}
(new plain())->fly();
// 飛行
// 思考: 接口自己並不提供實現,只要提供一個規範。
// php中,接口的語義是有限的,使用接口的地方並很少,php中接口能夠淡化爲設計文檔,起到一個團隊基本契約的做用
// 因爲php是弱類型,且強調靈活,因此並不推薦大規模使用接口,而僅在部分"內核"代碼中使用接口。從語義上考慮,
// 能夠更多地使用抽象類

//抽象類
abstract class Fruits{
    // 水果名稱
    protected $name;
    // 抽象方法
    abstract public function eat();
    // 儘管不能實例化抽象類,但仍然能夠有構造方法
    public function __construct(){
        echo "抽象構造器,實例化時自動調用" ;
    }
}
class Apple extends Fruits{
    protected $name = "蘋果";
    public function eat(){
        echo $this->name . "能夠直接生吃";
    }
    // 子類構造方法
    public function __construct()
    {
        echo parent::__construct();
    }
}
$apple = new Apple();
echo $apple->eat();
// 抽象構造器,實例化時自動調用
// 蘋果能夠直接生吃

// 抽象類提供了具體實現的標準,而接口則是純粹的模板

/*反射*/
/*
 * 面向對象編程中對象被賦予了自省的能力,而這個自省的過程就是反射。
 * 反射,直觀理解就是根據到達地找到出發地和來源。比方說:我給你一個光禿禿的對象,我能夠
 * 僅僅經過這個對象就能直到它所屬的類,擁有哪些方法
 */

// 反射API
$reflect = new ReflectionObject($apple);
// 獲取對象屬性列表
$props = $reflect->getProperties();
foreach ($props as $prop){
    var_dump($prop->getName());
}
// string(4) "name"
// 獲取對象的方法列表
$m = $reflect->getMethods();
foreach ($m as $prop){
    var_dump($prop->getName());
}
//  string(3) "eat"
//  string(11) "__construct"

// 也能夠不用反射API,使用class函數,返回對象屬性的關聯數組以及更多信息
var_dump(get_object_vars($apple)); // 返回對象屬性的關聯數組
// array(0) { }
var_dump(get_class_vars(get_class($apple))); // 類屬性
// array(0) { }
var_dump(get_class_methods(get_class($apple))); // 返回由類的方法名組成的數組
// array(2) { [0]=> string(3) "eat" [1]=> string(11) "__construct" }

// 在此。利用強大的反射API功能範元這個類的原型,包括方法的訪問權限:
$obj = new ReflectionClass('Apple');
// 獲取類型
$className = $obj -> getName();
// 初始化方法和成量數組
$methods = $properties = [];
foreach ($obj->getProperties() as $v){
    $properties[$v->getName()] = $v;
}
foreach ($obj->getMethods() as $v){
    $methods[$v->getName()] = $v;
}
echo "<br />class {$className}<br />{<br />";
is_array($properties) && ksort($properties); // 數組排序
// 遍歷出類的屬性
foreach ($properties as $k => $v){
    echo "\t";
    echo $v->isPublic() ? 'public' : '',$v->isPrivate() ? 'private' : '',$v->isProtected() ? 'protected' : '',
    $v->isStatic() ? 'static' : '';
    echo "\t{$k}";
}
// 遍歷出類的方法
is_array($methods) && ksort($methods); // 數組排序
foreach ($methods as $k => $v){
    echo "<br />function $k(){}";
}
echo "<br / >}";

/*
    class Apple{
        protected    name
        function __construct(){}
        function eat(){}
    }
 */
/*
 * 反射的做用: 用於文檔生成。所以能夠用它對文件裏的類進行掃描,逐個生成描述文檔
 *
相關文章
相關標籤/搜索