PHP高级特性如何使用
这篇文章主要介绍“PHP高级特性如何使用”,在日常操作中,相信很多人在PHP高级特性如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PHP高级特性如何使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
PHP高级特性-反射Reflection以及Factory工厂设计模式的结合使用[代码实例]
PHP高级特性-反射以及工厂设计模式的结合使用 [结合 Laravel-Admin 代码实例讲解]
利用反射来实现工厂模式的生产而无需创建特定的工厂类
反射[Relfection]
什么是Reflection
Reflection,即反射。反射提供给面向对象编程可以自省的能力
这么理解有点太过于概念化,通俗地讲,就是能根据事件的结果反查出原因。在编程中,可以根据一个被实例化的对象,反查出这个对象属于的类以及该类拥有所有属性以及方法,甚至可以读取文档注释。这个反查的过程就叫做反射
PHP 提供了完整的反射 API ,提供了内省类、接口、函数、方法和扩展的能力。此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。详细见PHP官网 PHP反射简介
Reflection能干什么
在上面讲到的,可以使用反射来获取一个类的所有属性以及方法还有注释文档,甚至可以获取类属性和方法的访问权限[protected/private],这些特性使得PHP的使用灵活性得到非常大的提高。例如:
– Laravel 框架的所谓优雅所在,即容器、依赖注入、IOC 控制反转就是依靠这些特性实现的
– Hyperf 框架的注解路由也是根据反射获得注释来实现的
– 生成文档 因为反射可以获取类属性和方法的访问权限,可以扫描整个项目的所有文件再使用反射来生成文档
– 测试驱动开发 利用反射获取该类的所有方法的特性,进行测试驱动开发
– 开发插件 利用反射获取类的内部结构的特性,实现 Hook 功能,例如框架插件的实现
Reflection的优缺点
优点 反射提供了对类的反解析,从而相比原本面向对象的编程方式获得了极高的灵活性,以及合理的使用能够让代码看起来更加优雅以及简洁。原本在面向对象的编程方式中,使用一个类的实例需要先 new 出一个对象再使用方法,但是使用了反射机制,只需要提供一个该类的方法然后使用反射机制即可使用该对象或者方法。Laravel 框架正是使用了大量的反射才获得了优雅的美誉,Swoole 的 Hyperf 框架的注解路由的实现也是使用了反射
缺点 同时,由于反射是类实例化的反过程,破坏了面向对象的封装性,直接将类的整个内部结构暴露,这就导致了反射一旦滥用,代码将难于管理,整个项目将非常混乱,甚至导致业务执行错乱。尤其在大项目几十人的团队中,试想一下,原本的面向对象,只告诉什么可以用,什么不可以用,CTO写好了底层代码,其他人继承后然后使用就行,内部结构啥的其他人都不知道。一旦用上了反射,如果有一个程序员不小心将原本是 protected 或者是 private 的属性或者方法设置成了可以访问,其他程序员在不知情的情况调用了本该隐藏的数据或者方法,那将导致不可预测的灾难【见下面示例代码】
其次,由于反射的灵活性极高,这导致了无法在 IDE 中通过直接直接点击代码溯源,对于新手真的是很蛋疼,Laravel 和Hyperf 都是如此
在下面的代码中,反射的机制直接将 private 方法设置成外部可访问
#Example:<?phpclass Foo { private function myPrivateMethod() { return 7; }}$method = new ReflectionMethod('Foo', 'myPrivateMethod');//该反射功能直接将原本是private权限的方法设置成可访问$method->setAccessible(true);echo $method->invoke(new Foo);// echos "7"?>
工厂设计模式
三种工厂设计模式 [简单工厂模式] [工厂模式] [抽象工厂模式]
简单工厂模式 又称为静态工厂方法模式。简单的说,就是创建对象的方式是通过一个静态方法来实现的。在简单工厂模式中,根据传递的参数来返回不同的类的实例
在PHP中在简单工厂模式中,有一个抽象的产品类【即abstract class Calculate】,这个抽象类可以是接口/抽象类/普通类。这个抽象的产品类可以派生出多个具体的产品类【即class CalculateAdd以及class CalculateSub】。最后再由一个具体的工厂类【即class CalculateFactory】来获取所需要的产品类的实例
代码实现
1) 抽象产品生产类:运算抽象类
//生产抽象类abstract class Calculate{ //数字A protected $number_a = null; //数字B protected $number_b = null; //设置数字A public function setNumberA( $number ){ $this->number_a = $number; } //设置数字B public function setNumberB( $number ){ $this->number_b = $number; } //获取数字A public function getNumberA(){ return $this->number_a; } //获取数字B public function getNumberB(){ return $this->number_b; } //获取计算结果【获取生产出的产品】 public function getResult(){ return null; }}
2) 具体产品生产类:加法运算 / 减法运算 等等
//加法运算class CalculateAdd extends Calculate{ //获取运算结果【获取具体的产品】 public function getResult(){ return $this->number_a + $this->number_b; }}//减法运算class CalculateSub extends Calculate{ //获取运算结果【获取具体的产品】 public function getResult(){ return $this->number_a - $this->number_b; }}//乘法 / 除法 等等其他运算【其他产品】
3) 工厂:工厂类。即用一个单独的类来创造实例化的过程,这个类就是工厂。也就是 简单工厂模式
在 php 中,实现的方式其实就一个 switch 函数或者是 php8 新出的 match 函数来实例化所需要的产品生产类
//根据运算不同实例化不同的对象//【也就是根据所需产品,实例化对应的产品类进行生产】//对应的实现其实就是一个switch或者php8函数新出的match函数//下面用最新的match函数做演示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
总结:
简单工厂模式其实就是创建一个基类【abstract】,该类存放所有具体生产产品类的共用的代码,但是没有执行过程,然后具体生产产品的类全部继承基类再实现各自的生产过程。最后创建一个工厂类,该类用来根据传入的参数来获取所需的生产类
工厂方法模式 又称为工厂模式,属于创造型模式。在工厂模式中,工厂类的父类只负责定义公共接口,并不执行实际的生产动作。实际的生产动作则交给工厂的子类来完成。这样做将类的的实例化延迟到了工厂的子类,通过工厂的子类来完成实例化具体的产品,也就是生产
在工厂模式中,跟简单工厂模式不一样的是,有一个抽象的工厂类【即interface CalculateFactory】,可以是接口/抽象类,这个抽象的工厂类可以派生出多个具体的工厂类【即FactoryAdd以及FactorySub】
代码实现【以下代码需要用到上面的生产抽象类】
以下代码需要用到上面的生产抽象类:abstract class Calculate
以及具体的生产类,即:CalculateAdd 以及 CalculateSub。下面不再重复实现
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(); }}//具体使用//创建工厂实例$calculateFactory = new FactoryAdd();$add = $calculateFactory->CreateCalculate();$add->setNumberA( 1 );$add->setNumberB( 2 );//计算echo $add->getResult();//echo 3
总结:
工厂模式相比于简单工厂模式的区别在于,在简单工厂模式中,只有一个工厂来生产对应的生产对象【即CalculateFactory】。而在工厂模式中,每一个生产产对象都由自己的工厂来生产,并且这些工厂都继承自同一个接口【即 interface CalculateFactory】
抽象工厂模式 抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而且无需指定它们具体的类。这么理解很抽象。通俗一点的解释就是,相比于上面的工厂模式来讲,抽象工厂模式在每个不同的工厂之上又有一个超级工厂,这个超级工厂是抽象的接口【interface】,用来生产具体的工厂
在抽象工厂模式中,有多个抽象的产品类【即abstract class Phone以及abstract class Android】,可以是接口/抽象类/普通类,每个抽象产品类可以派生出多个具体产品类【即class IPhone / class MiPhone 以及 class IOS / class Android】。一个抽象的工厂类【即interface AbstractFactory】可以派生出多个具体的工厂类【即class iPhoneFactory以及class MiFactory】,且每个具体的工厂类可以创建多个产品类的实例【即都有createPhone和createSystem】
代码实现
//抽象的产品类abstract class Phone{}abstract class System{}//具体的产品类class IPhone extends Phone{}class MiPhone extends Phone{}//具体的产品类class IOS extends System{}class Android extends System{}//超级工厂interface AbstractFactory{ public function createPhone(); public function createSystem();}//具体的苹果工厂class iPhoneFactory implements AbstractFactory{ //生产苹果手机 public function createPhone(){ return new IPhone(); } //生产苹果系统 public function createSystem(){ return new IOS(); }}//具体的小米工厂class MiFactory implements AbstractFactory{ //生产小米手机 public function createPhone(){ return new MiPhone(); } //生产安卓系统 public function createSystem(){ return new Android(); }}
总结:
抽象工厂模式相比于工厂模式,抽象工厂模式提供了一个接口用来规定所需要生产的产品。每个继承于该接口的工厂都能按照指定的模式进行生产【代码中的AbstarctFactory】
以上三种工厂模式,最终都是为了将重复的代码提取出来,并且按照特定的需求场景归纳好,进行解耦和复用,以便在需要的场景中直接使用
三种模式的概括为:
简单工厂:
一个抽象产品类(可以是:接口,抽象类,普通类),可以派生出多个具体产品类
单独一个具体的工厂类
每个具体工厂类只能创建一个具体产品类的实例
工厂模式:
一个抽象产品类(可以是:接口,抽象类,普通类),可以派生出多个具体产品类
一个抽象工厂类(可以是:接口,抽象类),可以派生出多个具体工厂类
每个具体工厂类只能创建一个具体产品类的实例
抽象工厂:
多个抽象产品类(可以是:接口,抽象类,普通类),每个抽象产品类可以派生出多个具体产品类
一个抽象工厂类(可以是:接口,抽象类),可以派生出多个具体工厂类
每个具体工厂类可以创建多个具体产品类的实例
三个模式之间的区别:
简单工厂模式只有一个抽象产品类,只有一个具体的工厂类
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个具体产品类的实例
工厂模式与反射的结合使用
可以利用反射的特性来实现工厂模式的生产过程,结合Laravel-admin进行举例
先看下以下的代码,需求背景:需要根据角色不同显示不同的权限按钮
<?phpclass TaskController extends BaseController{ use HasResourceActions; 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(); } }}
以上的代码很明显一看就显得很臃肿。且随着业务的增加【即Controller的增加】以及角色的增加,需要写更多重复的判断以及重复的代码
解决思路:
不同的角色需要拥有的不同的权限,每个角色都可以用一个固定的方法来设置权限,这个固定的方法可以为不同的角色设置权限。这些条件刚好满足工厂模式的使用场景:即:
抽象出一个产品类来派生出多个角色的权限产品类
抽象出一个工厂类来派生出多个具体的工厂类,这些工厂类表现为对应要使用权限按钮的场景
每个具体工厂【使用权限按钮的场景】可以创建多个具体产品类【即实例化多个角色的权限产品】
代码如下【在下面的代码中,将使用反射来代替工厂的生产】
1) 抽象出一个产品类来派生出多个角色的权限产品类
<?phpnamespace App\GridActionFactory;use Dcat\Admin\Grid;interface GridActionInterface{ //业务员角色的权限 function salesmanAction(Grid $grid); //分配员角色的权限 function assignmentAction(Grid $grid); //财务角色的权限 function financeAction(Grid $grid); //....其他角色的权限}
2,3) 2,3两个步骤包含在一起。抽象出一个工厂类来派生出多个具体的工厂类,这些工厂类表现为对应要使用权限按钮的场景。其中,setRoleAction方法使用反射来直接生产,也就是替代了每个具体工厂类创建实例的过程
<?phpnamespace App\GridActionFactory;use Dcat\Admin\Admin;use Dcat\Admin\Grid;abstract class GridActionAbstract{ // abstract public static function setAction(Grid $grid, string $role); protected static function isInRoles(string $role): bool { return Admin::user()->inRoles([$role]); } 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); }}
根据以上的反射来实现实例化的过程,上面的TaskController的权限可以简化成下面的代码:
<?phpnamespace App\GridActionFactory;use Dcat\Admin\Grid;class TaskAction extends GridActionAbstract implements GridActionInterface{ public static function setAction(Grid $grid, string $role) { if (!parent::isInRoles($role)) return; //通过调用父类的setRoleAction直接实现生产的过程 parent::setRoleAction($grid, $role, self::class); } //在TaskController下有需要使用权限按钮的角色 //分配员角色 public function assignmentAction(Grid $grid) { //权限按钮 $grid->showActions(); $grid->showViewButton(); } //在TaskController下有需要使用权限按钮的角色 //财务角色 public function financeAction(Grid $grid) { $grid->showActions(); $grid->showViewButton(); } //在TaskController下有需要使用权限按钮的角色 //业务员角色 public function salesmanAction(Grid $grid) { } //....其他角色}
经过使用设计模式封装后,上面TaskController中控制权限的代码直接优化成如下:【优雅了不少~】
<?phpclass TaskController extends BaseController{ use HasResourceActions; protected function grid() { //Grid Columns... //财务角色按钮 TaskAction::setAction($grid, AdminUserModel::getFinanceRole()); //分配员角色按钮 TaskAction::setAction($grid, AdminUserModel::getAssignmentRole()); //...其他角色按钮 }}
到此,关于“PHP高级特性如何使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341