您好,登錄后才能下訂單哦!
這篇文章主要介紹“PHP高級特性如何使用”,在日常操作中,相信很多人在PHP高級特性如何使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PHP高級特性如何使用”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
PHP高級特性-反射以及工廠設(shè)計模式的結(jié)合使用 [結(jié)合 Laravel-Admin 代碼實例講解]
利用反射來實現(xiàn)工廠模式的生產(chǎn)而無需創(chuàng)建特定的工廠類
反射[Relfection]
什么是Reflection
Reflection,即反射。反射提供給面向?qū)ο缶幊炭梢宰允〉哪芰?/p>
這么理解有點太過于概念化,通俗地講,就是能根據(jù)事件的結(jié)果反查出原因。在編程中,可以根據(jù)一個被實例化的對象,反查出這個對象屬于的類以及該類擁有所有屬性以及方法,甚至可以讀取文檔注釋。這個反查的過程就叫做反射
PHP 提供了完整的反射 API ,提供了內(nèi)省類、接口、函數(shù)、方法和擴展的能力。此外,反射 API 提供了方法來取出函數(shù)、類和方法中的文檔注釋。詳細見PHP官網(wǎng) PHP反射簡介
Reflection能干什么
在上面講到的,可以使用反射來獲取一個類的所有屬性以及方法還有注釋文檔,甚至可以獲取類屬性和方法的訪問權(quán)限[protected/private],這些特性使得PHP的使用靈活性得到非常大的提高。例如:
– Laravel 框架的所謂優(yōu)雅所在,即容器、依賴注入、IOC 控制反轉(zhuǎn)就是依靠這些特性實現(xiàn)的
– Hyperf 框架的注解路由也是根據(jù)反射獲得注釋來實現(xiàn)的
– 生成文檔 因為反射可以獲取類屬性和方法的訪問權(quán)限,可以掃描整個項目的所有文件再使用反射來生成文檔
– 測試驅(qū)動開發(fā) 利用反射獲取該類的所有方法的特性,進行測試驅(qū)動開發(fā)
– 開發(fā)插件 利用反射獲取類的內(nèi)部結(jié)構(gòu)的特性,實現(xiàn) Hook 功能,例如框架插件的實現(xiàn)
Reflection的優(yōu)缺點
優(yōu)點 反射提供了對類的反解析,從而相比原本面向?qū)ο蟮木幊谭绞将@得了極高的靈活性,以及合理的使用能夠讓代碼看起來更加優(yōu)雅以及簡潔。原本在面向?qū)ο蟮木幊谭绞街校褂靡粋€類的實例需要先 new 出一個對象再使用方法,但是使用了反射機制,只需要提供一個該類的方法然后使用反射機制即可使用該對象或者方法。Laravel 框架正是使用了大量的反射才獲得了優(yōu)雅的美譽,Swoole 的 Hyperf 框架的注解路由的實現(xiàn)也是使用了反射
缺點 同時,由于反射是類實例化的反過程,破壞了面向?qū)ο蟮姆庋b性,直接將類的整個內(nèi)部結(jié)構(gòu)暴露,這就導(dǎo)致了反射一旦濫用,代碼將難于管理,整個項目將非常混亂,甚至導(dǎo)致業(yè)務(wù)執(zhí)行錯亂。尤其在大項目幾十人的團隊中,試想一下,原本的面向?qū)ο?,只告訴什么可以用,什么不可以用,CTO寫好了底層代碼,其他人繼承后然后使用就行,內(nèi)部結(jié)構(gòu)啥的其他人都不知道。一旦用上了反射,如果有一個程序員不小心將原本是 protected 或者是 private 的屬性或者方法設(shè)置成了可以訪問,其他程序員在不知情的情況調(diào)用了本該隱藏的數(shù)據(jù)或者方法,那將導(dǎo)致不可預(yù)測的災(zāi)難【見下面示例代碼】
其次,由于反射的靈活性極高,這導(dǎo)致了無法在 IDE 中通過直接直接點擊代碼溯源,對于新手真的是很蛋疼,Laravel 和Hyperf 都是如此
在下面的代碼中,反射的機制直接將 private 方法設(shè)置成外部可訪問
#Example: <?php class Foo { private function myPrivateMethod() { return 7; } } $method = new ReflectionMethod('Foo', 'myPrivateMethod'); //該反射功能直接將原本是private權(quán)限的方法設(shè)置成可訪問 $method->setAccessible(true); echo $method->invoke(new Foo); // echos "7" ?>
工廠設(shè)計模式
三種工廠設(shè)計模式 [簡單工廠模式] [工廠模式] [抽象工廠模式]
簡單工廠模式 又稱為靜態(tài)工廠方法模式。簡單的說,就是創(chuàng)建對象的方式是通過一個靜態(tài)方法來實現(xiàn)的。在簡單工廠模式中,根據(jù)傳遞的參數(shù)來返回不同的類的實例
在PHP中在簡單工廠模式中,有一個抽象的產(chǎn)品類【即abstract class Calculate】,這個抽象類可以是接口/抽象類/普通類。這個抽象的產(chǎn)品類可以派生出多個具體的產(chǎn)品類【即class CalculateAdd以及class CalculateSub】。最后再由一個具體的工廠類【即class CalculateFactory】來獲取所需要的產(chǎn)品類的實例
代碼實現(xiàn)
1) 抽象產(chǎn)品生產(chǎn)類:運算抽象類
//生產(chǎn)抽象類 abstract class Calculate{ //數(shù)字A protected $number_a = null; //數(shù)字B protected $number_b = null; //設(shè)置數(shù)字A public function setNumberA( $number ){ $this->number_a = $number; } //設(shè)置數(shù)字B public function setNumberB( $number ){ $this->number_b = $number; } //獲取數(shù)字A public function getNumberA(){ return $this->number_a; } //獲取數(shù)字B public function getNumberB(){ return $this->number_b; } //獲取計算結(jié)果【獲取生產(chǎn)出的產(chǎn)品】 public function getResult(){ return null; } }
2) 具體產(chǎn)品生產(chǎn)類:加法運算 / 減法運算 等等
//加法運算 class CalculateAdd extends Calculate{ //獲取運算結(jié)果【獲取具體的產(chǎn)品】 public function getResult(){ return $this->number_a + $this->number_b; } } //減法運算 class CalculateSub extends Calculate{ //獲取運算結(jié)果【獲取具體的產(chǎn)品】 public function getResult(){ return $this->number_a - $this->number_b; } } //乘法 / 除法 等等其他運算【其他產(chǎn)品】
3) 工廠:工廠類。即用一個單獨的類來創(chuàng)造實例化的過程,這個類就是工廠。也就是 簡單工廠模式
在 php 中,實現(xiàn)的方式其實就一個 switch 函數(shù)或者是 php8 新出的 match 函數(shù)來實例化所需要的產(chǎn)品生產(chǎn)類
//根據(jù)運算不同實例化不同的對象 //【也就是根據(jù)所需產(chǎn)品,實例化對應(yīng)的產(chǎn)品類進行生產(chǎn)】 //對應(yīng)的實現(xiàn)其實就是一個switch或者php8函數(shù)新出的match函數(shù) //下面用最新的match函數(shù)做演示 class CalculateFactory{ public static function setCalculate( $type = null ){ return match( $type ){ 'add' => (function(){ return new CalculateAdd(); })(), 'sub' => (function(){ return new CalculateSub(); })(), default => null; }; } } //具體使用 $calculate = CalculateFactory::setCalculate('add'); $calculate->setNumberA = 1; $calculate->setNumberB = 2; //計算 echo $calculate->getResult;//echo 3
總結(jié):
簡單工廠模式其實就是創(chuàng)建一個基類【abstract】,該類存放所有具體生產(chǎn)產(chǎn)品類的共用的代碼,但是沒有執(zhí)行過程,然后具體生產(chǎn)產(chǎn)品的類全部繼承基類再實現(xiàn)各自的生產(chǎn)過程。最后創(chuàng)建一個工廠類,該類用來根據(jù)傳入的參數(shù)來獲取所需的生產(chǎn)類
工廠方法模式 又稱為工廠模式,屬于創(chuàng)造型模式。在工廠模式中,工廠類的父類只負責(zé)定義公共接口,并不執(zhí)行實際的生產(chǎn)動作。實際的生產(chǎn)動作則交給工廠的子類來完成。這樣做將類的的實例化延遲到了工廠的子類,通過工廠的子類來完成實例化具體的產(chǎn)品,也就是生產(chǎn)
在工廠模式中,跟簡單工廠模式不一樣的是,有一個抽象的工廠類【即interface CalculateFactory】,可以是接口/抽象類,這個抽象的工廠類可以派生出多個具體的工廠類【即FactoryAdd以及FactorySub】
代碼實現(xiàn)【以下代碼需要用到上面的生產(chǎn)抽象類】
以下代碼需要用到上面的生產(chǎn)抽象類:abstract class Calculate
以及具體的生產(chǎn)類,即:CalculateAdd 以及 CalculateSub。下面不再重復(fù)實現(xiàn)
interface CalculateFactory{ public function CreateCalculate(); } class FactoryAdd implements CalculateFactory{ public function CreateCalculate(){ return new CalculateAdd(); } } class FactorySub implements CalculateFactory{ public function CreateCalculate(){ return new CalculateSub(); } } //具體使用 //創(chuàng)建工廠實例 $calculateFactory = new FactoryAdd(); $add = $calculateFactory->CreateCalculate(); $add->setNumberA( 1 ); $add->setNumberB( 2 ); //計算 echo $add->getResult();//echo 3
總結(jié):
工廠模式相比于簡單工廠模式的區(qū)別在于,在簡單工廠模式中,只有一個工廠來生產(chǎn)對應(yīng)的生產(chǎn)對象【即CalculateFactory】。而在工廠模式中,每一個生產(chǎn)產(chǎn)對象都由自己的工廠來生產(chǎn),并且這些工廠都繼承自同一個接口【即 interface CalculateFactory】
抽象工廠模式 抽象工廠模式提供創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而且無需指定它們具體的類。這么理解很抽象。通俗一點的解釋就是,相比于上面的工廠模式來講,抽象工廠模式在每個不同的工廠之上又有一個超級工廠,這個超級工廠是抽象的接口【interface】,用來生產(chǎn)具體的工廠
在抽象工廠模式中,有多個抽象的產(chǎn)品類【即abstract class Phone以及abstract class Android】,可以是接口/抽象類/普通類,每個抽象產(chǎn)品類可以派生出多個具體產(chǎn)品類【即class IPhone / class MiPhone 以及 class IOS / class Android】。一個抽象的工廠類【即interface AbstractFactory】可以派生出多個具體的工廠類【即class iPhoneFactory以及class MiFactory】,且每個具體的工廠類可以創(chuàng)建多個產(chǎn)品類的實例【即都有createPhone和createSystem】
代碼實現(xiàn)
//抽象的產(chǎn)品類 abstract class Phone{} abstract class System{} //具體的產(chǎn)品類 class IPhone extends Phone{} class MiPhone extends Phone{} //具體的產(chǎn)品類 class IOS extends System{} class Android extends System{} //超級工廠 interface AbstractFactory{ public function createPhone(); public function createSystem(); } //具體的蘋果工廠 class iPhoneFactory implements AbstractFactory{ //生產(chǎn)蘋果手機 public function createPhone(){ return new IPhone(); } //生產(chǎn)蘋果系統(tǒng) public function createSystem(){ return new IOS(); } } //具體的小米工廠 class MiFactory implements AbstractFactory{ //生產(chǎn)小米手機 public function createPhone(){ return new MiPhone(); } //生產(chǎn)安卓系統(tǒng) public function createSystem(){ return new Android(); } }
總結(jié):
抽象工廠模式相比于工廠模式,抽象工廠模式提供了一個接口用來規(guī)定所需要生產(chǎn)的產(chǎn)品。每個繼承于該接口的工廠都能按照指定的模式進行生產(chǎn)【代碼中的AbstarctFactory】
以上三種工廠模式,最終都是為了將重復(fù)的代碼提取出來,并且按照特定的需求場景歸納好,進行解耦和復(fù)用,以便在需要的場景中直接使用
三種模式的概括為:
簡單工廠:
一個抽象產(chǎn)品類(可以是:接口,抽象類,普通類),可以派生出多個具體產(chǎn)品類
單獨一個具體的工廠類
每個具體工廠類只能創(chuàng)建一個具體產(chǎn)品類的實例
工廠模式:
一個抽象產(chǎn)品類(可以是:接口,抽象類,普通類),可以派生出多個具體產(chǎn)品類
一個抽象工廠類(可以是:接口,抽象類),可以派生出多個具體工廠類
每個具體工廠類只能創(chuàng)建一個具體產(chǎn)品類的實例
抽象工廠:
多個抽象產(chǎn)品類(可以是:接口,抽象類,普通類),每個抽象產(chǎn)品類可以派生出多個具體產(chǎn)品類
一個抽象工廠類(可以是:接口,抽象類),可以派生出多個具體工廠類
每個具體工廠類可以創(chuàng)建多個具體產(chǎn)品類的實例
三個模式之間的區(qū)別:
簡單工廠模式只有一個抽象產(chǎn)品類,只有一個具體的工廠類
工廠方法模式只有一個抽象產(chǎn)品類,而抽象工廠模式有多個抽象產(chǎn)品類
工廠方法模式的具體工廠類只能創(chuàng)建一個具體產(chǎn)品類的實例,而抽象工廠模式可以創(chuàng)建多個具體產(chǎn)品類的實例
工廠模式與反射的結(jié)合使用
可以利用反射的特性來實現(xiàn)工廠模式的生產(chǎn)過程,結(jié)合Laravel-admin進行舉例
先看下以下的代碼,需求背景:需要根據(jù)角色不同顯示不同的權(quán)限按鈕
<?php class TaskController extends BaseController { use HasResourceActions; /** * Make a grid builder. * * @return Grid */ protected function grid() { //Grid Columns... if (Admin::user()->inRoles([AdminUserModel::getAssignmentRole()])) { $grid->disableBatchActions(); $grid->disableEditButton(); $grid->disableCreateButton(); $grid->disableDeleteButton(); } elseif (Admin::user()->inRoles([AdminUserModel::getEvaluatorRole()])) { $grid->disableBatchActions(); $grid->disableEditButton(); $grid->disableCreateButton(); $grid->disableDeleteButton(); $grid->actions(function (Grid\Displayers\Actions $actions) { $actions->append(new ConfirmCloseTaskAction()); }); } else { $grid->disableCreateButton(); $grid->disableDeleteButton(); $grid->disableEditButton(); $grid->disableBatchActions(); $grid->disableViewButton(); $grid->disableActions(); } } }
以上的代碼很明顯一看就顯得很臃腫。且隨著業(yè)務(wù)的增加【即Controller的增加】以及角色的增加,需要寫更多重復(fù)的判斷以及重復(fù)的代碼
解決思路:
不同的角色需要擁有的不同的權(quán)限,每個角色都可以用一個固定的方法來設(shè)置權(quán)限,這個固定的方法可以為不同的角色設(shè)置權(quán)限。這些條件剛好滿足工廠模式的使用場景:即:
抽象出一個產(chǎn)品類來派生出多個角色的權(quán)限產(chǎn)品類
抽象出一個工廠類來派生出多個具體的工廠類,這些工廠類表現(xiàn)為對應(yīng)要使用權(quán)限按鈕的場景
每個具體工廠【使用權(quán)限按鈕的場景】可以創(chuàng)建多個具體產(chǎn)品類【即實例化多個角色的權(quán)限產(chǎn)品】
代碼如下【在下面的代碼中,將使用反射來代替工廠的生產(chǎn)】
1) 抽象出一個產(chǎn)品類來派生出多個角色的權(quán)限產(chǎn)品類
<?php namespace App\GridActionFactory; use Dcat\Admin\Grid; /** * 工廠接口 */ interface GridActionInterface { //業(yè)務(wù)員角色的權(quán)限 function salesmanAction(Grid $grid); //分配員角色的權(quán)限 function assignmentAction(Grid $grid); //財務(wù)角色的權(quán)限 function financeAction(Grid $grid); //....其他角色的權(quán)限 }
2,3) 2,3兩個步驟包含在一起。抽象出一個工廠類來派生出多個具體的工廠類,這些工廠類表現(xiàn)為對應(yīng)要使用權(quán)限按鈕的場景。其中,setRoleAction方法使用反射來直接生產(chǎn),也就是替代了每個具體工廠類創(chuàng)建實例的過程
<?php namespace App\GridActionFactory; use Dcat\Admin\Admin; use Dcat\Admin\Grid; /** * 設(shè)置Action權(quán)限抽象類 */ abstract class GridActionAbstract { // abstract public static function setAction(Grid $grid, string $role); /** * 過濾角色 * * @param string $role * @return bool */ protected static function isInRoles(string $role): bool { return Admin::user()->inRoles([$role]); } /** * 調(diào)用對應(yīng)的方法 * [該方法其實就是工廠模式中的工廠,專門來生產(chǎn)的] * [多個工廠對應(yīng)的就是各個需要用到Action權(quán)限的Controller控制器] * [每個Controller控制器來生產(chǎn)自己的Action權(quán)限] * [這個生產(chǎn)是通過反射來實現(xiàn)] * * @param Grid $grid * @param string $role * @param string $class * @throws \ReflectionException */ protected static function setRoleAction(Grid $grid, string $role, string $class) { $r = new \ReflectionClass($class); $methodName = $role . 'Action'; if (!$r->hasMethod($methodName)) throw new \Exception('Method Not Found [ method : ' . $methodName . ' ] '); $method = $r->getMethod($methodName); $method->invoke($r->newInstance(), $grid); } }
根據(jù)以上的反射來實現(xiàn)實例化的過程,上面的TaskController的權(quán)限可以簡化成下面的代碼:
<?php namespace App\GridActionFactory; use Dcat\Admin\Grid; class TaskAction extends GridActionAbstract implements GridActionInterface { /** * @param Grid $grid * @param string $role * @throws \ReflectionException */ public static function setAction(Grid $grid, string $role) { if (!parent::isInRoles($role)) return; //通過調(diào)用父類的setRoleAction直接實現(xiàn)生產(chǎn)的過程 parent::setRoleAction($grid, $role, self::class); } //在TaskController下有需要使用權(quán)限按鈕的角色 //分配員角色 public function assignmentAction(Grid $grid) { //權(quán)限按鈕 $grid->showActions(); $grid->showViewButton(); } //在TaskController下有需要使用權(quán)限按鈕的角色 //財務(wù)角色 public function financeAction(Grid $grid) { $grid->showActions(); $grid->showViewButton(); } //在TaskController下有需要使用權(quán)限按鈕的角色 //業(yè)務(wù)員角色 public function salesmanAction(Grid $grid) { } //....其他角色 }
經(jīng)過使用設(shè)計模式封裝后,上面TaskController中控制權(quán)限的代碼直接優(yōu)化成如下:【優(yōu)雅了不少~】
<?php class TaskController extends BaseController { use HasResourceActions; /** * Make a grid builder. * * @return Grid */ protected function grid() { //Grid Columns... //財務(wù)角色按鈕 TaskAction::setAction($grid, AdminUserModel::getFinanceRole()); //分配員角色按鈕 TaskAction::setAction($grid, AdminUserModel::getAssignmentRole()); //...其他角色按鈕 /* if (Admin::user()->inRoles([AdminUserModel::getAssignmentRole()])) { $grid->disableBatchActions(); $grid->disableEditButton(); $grid->disableCreateButton(); $grid->disableDeleteButton(); } elseif (Admin::user()->inRoles([AdminUserModel::getEvaluatorRole()])) { $grid->disableBatchActions(); $grid->disableEditButton(); $grid->disableCreateButton(); $grid->disableDeleteButton(); $grid->actions(function (Grid\Displayers\Actions $actions) { $actions->append(new ConfirmCloseTaskAction()); }); } else { $grid->disableCreateButton(); $grid->disableDeleteButton(); $grid->disableEditButton(); $grid->disableBatchActions(); $grid->disableViewButton(); $grid->disableActions(); } */ } }
到此,關(guān)于“PHP高級特性如何使用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。