本文首发于Rootrl'sBlog介绍Laravel是一个先进的现代框架,有些概念非常重要。在开始使用Laravel之前,我认为有必要先了解一下这些概念。您甚至需要更新您的PHPOOP知识。相信很多人对gettersetter和__invoke、__call、__callStatic等魔术方法的作用,甚至this、self、static等关键字的作用都还很模糊(我上个老板就喜欢问这样的基础问题,然后无法回答-_-')。DI&IoC先解释一下术语。DI的全称是Dependencyinjection,意思是依赖注入。而IoC就是Inversionofcontrol控制反转。要理解依赖注入和控制反转,首先不得不提到面向对象设计中的五个设计原则:S.O.L.I.D.S.O.L.I.D-面向对象设计五原则SRP单一职责原则OCP开闭原则LSP里氏代换原则ISP接口隔离原则DIP依赖??倒置原则五原则这个思想原则对于我们平时的软件开发设计来说非常重要,你可以详细了解它。依赖倒置原则这里我们主要关注依赖倒置原则:实体必须依赖抽象而不是具体的实现。这意味着高层模块不应该依赖于低层模块,它们都应该依赖于抽象。在传统的软件设计中,我们一般都是依赖上层代码来依赖下层代码。当下层代码发生变化时,我们的上层代码也必须随之变化,维护成本比较高。这时候我们可以在上层定义接口,在下层实现这个接口,使得下层依赖于上层,降低耦合度。(PC主板和键鼠接口就是一个很好的例子,每个数据厂商都是根据主板上的接口生产自己的键鼠产品,这样如果鼠标坏了,我们可以换一个鼠标即在不修改主板上的接口的情况下满足接口要求是什么)InversionofControl上面说的依赖倒置是一个原理,控制倒置是实现依赖倒置的具体方法。控制反转的核心是将上层(类)的依赖单元的实例化过程交给第三方,类中不允许有依赖单元的实例化语句。例如:classComment{...publicfunctionafterInsert(){$notification=newEmailNotification(...);$通知->发送(...);}}如上,如果我们在用户提交评论后通知给评论者,这里的通知方式是email,在类中直接实例化email通知类,所以代码耦合度高。如果更改短信通知方式,则必须更改此处的代码。具体实现我们下面说。依赖注入依赖注入是一种设计模式,是IoC的具体实现。IoC的实现自然符合依赖倒置的原则。依赖注入的核心思想是在类外部实现类中依赖单元的实例化过程,然后向其中注入依赖。常用的依赖注入方法包括属性注入和构造函数注入。例如使用构造函数注入解耦上面的代码://NotificationinterfaceinterfaceNotifaction{publicfunctionsend(...);}//SMSnotificationimplementsnotificationinterfaceclassSmsNotificationimplementsNotification{publicfunctionsend(...){……}}//评论类类评论{...protected$notification;公共函数__construct(Notification$smsNotification){$this->notification=$smsNotification;}publicfunctionafterInsert(){$this->notification->send(...);}}//实例化短信通知类$smsNotification=newSmsNotification(...);//通过构造方法注入$comment=newComment($smsNotification);...$comment->save();这样,我们先定义了Notification接口,它有一个send方法,让后面的notifiers,不管是邮件类还是短信类,都实现这个接口,然后在外面通过构造函数注入,这样就解决了Comment类对具体通知方法的依赖,只要实现了Notification接口,就可以通过构造函数传入,而Comment类根本不需要修改。这样无论是代码维护还是单元测试都非常方便(可以模拟一个Notification类)。依赖注入是IoC的一种具体实现,是一种解耦的手段。当然,IoC的实现不止一种,比如Yii中的ServiceLocator(服务定位器)IoC容器/DI容器。当项目比较大的时候,像上面的评论类,通知类这样的依赖会很多,整个项目会很复杂。这时候就需要一个集中的地方来管理这些依赖。我们称之为IoC容器,提供动态创建、注入依赖单元、映射依赖等功能。这样可以集中依赖管理,减少大量代码。服务容器Laravel官方文档对服务容器的定义如下:Laravel服务容器是管理类依赖和进行依赖注入的工具。首先,服务容器通过DI依赖注入实现IoC,然后它还支持另一种实现方式:绑定和解析。绑定几乎所有的服务容器绑定操作都是在服务提供者(serviceprovider)中注册绑定的。服务提供者可以通过$this->app获取服务容器,然后使用服务容器提供的方法如$this->app->bind(...)等进行具体的服务绑定。类似支持的绑定方式有:simplebindingbindingsingletonbindinginstancebindinginitialdatabindinginterfacetoimplementcontextbindingmarkextensionbinding可以参考官方文档:https://laravel.com/docs/5.6/...绑定后是resolved,可以在使用之前从服务容器中解析对象。解析方法包括:通过make方法,接收一个你要解析的类或接口从数组中的容器中解析出对象自动注入实例先定义一个自己的类Foo{publicfunctionbar(){...}}我们简单地将Foo类绑定到服务容器:App::bind("foo",function($app){returnnewFoo();})通常在上下文中获取这个实例:$foo=App::制作(“富”);//$foo是Foo类的实例。当然,这种绑定和解析可以写在代码的任何地方,但是如果太多了,就会变得乱七八糟。所以我一开始就说了,几乎所有这种依赖服务绑定的操作都是在Serviceprovider中进行的。让我向您介绍服务提供商。服务提供者Serviceprovider为了让依赖注入的代码不被混淆,Laravel提供了一个服务提供者(ServiceProvider),它将这些依赖集合在一起,统一声明和管理,让依赖更容易维护。以下是抄袭官方的一些套话(-_-'),大家可以直接跳转到代码示例,再查看官方文档加深理解。所有服务提供者都需要继承IlluminateSupportServiceProvider类。大多数服务提供者都包含注册和引导方法。在register方法中,事务只能绑定到服务容器上。您不应尝试在注册方法中注册任何事件侦听器、路由或任何其他功能。可以为服务提供商的引导方法设置类型提示。服务容器将自动注入它需要的任何依赖项。引导方法将在所有其他服务提供者注册后调用。所有服务提供者都在config/app.php配置文件中注册。可以选择将服务提供者的注册推迟到实际需要注册绑定时,这可以提高应用程序性能。例子在前面的例子中,我们是在上下文中随意定义和获取的。下面我们作为服务提供者进行:useIlluminate\Support\ServiceProvider;classFooServiceProviderextendsServiceProvider{publicfunctionregister(){$this->app->bind('foo',function(){returnnewFoo();});上面实现了一个Foo服务提供者,我们可以手动将其注入到上下文中:App::register('FooServiceProvider');当然,我们更多的是通过配置文件,在app/config/app.php中的providers数组中添加一行:'providers'=>[...'FooServiceProvider',],这样我们就可以直接获取实例在上下文中:App::make('foo')当然我们也可以通过Facade的方式,更方便的操作Foo类。FacadesFacades实际上应用了设计模式中的外观模式:外观模式(Facade),它隐藏了系统的复杂性,为客户端提供了可以访问系统的接口。这种类型的设计模式是一种结构模式。为子系统中的一组接口提供统一的访问接口,使子系统更易于访问或使用。在Laravel中随处可见对这些静态方法的调用:$value=Cache::get('key');这些静态调用实际上并不是调用静态方法,而是通过PHP的魔术方法__callStatic()对应的方法传递请求。比如我们查看IlluminateSupportFacadesCache这个类,你会发现类中并没有get静态方法:classCacheextendsFacade{/***获取组件的注册名。**@returnstring*/受保护的静态函数getFacadeAccessor(){return'cache';}}这其中的奥秘就在基类Facade中:publicstaticfunction__callStatic($method,$args){//获取实例$instance=static::getFacadeRoot();if(!$instance){thrownewRuntimeException('Afacaderoothasnotbeenset.');}//实际调用对应的方法return$instance->$method(...$args);}有一个获取实例,然后调用具体方法的过程。例子继续前面的例子,我们通常通过App::make('foo')获取实例,然后调用具体的方法。现在我们通过门面来简化这个过程:首先定义一个门面:useIlluminate\Support\Facades\Facade;classFooextendsFacade{protectedstaticfunctiongetFacadeAccessor(){return'foo';}}然后我们就可以很方便的使用Foo类的一个方法:Foo::bar();ContractsContractsLaravel的契约是一组定义框架提供的核心服务的接口。未来这个接口可以有多种实现,解耦具体实现的依赖,在不改变代码逻辑的情况下获得更多的多态结果。比如你只需要在配置文件中指定你需要的缓存驱动(redis、memcached、file等),Laravel会自动为你切换到这个驱动,而不需要你针对某个特定的逻辑和代码进行改动司机。综上所述,这些都是一些基本的抽象概念,但是非常重要。这些思想在Laravel中随处可见,是所有实现的基石。在学习的过程中,基础很重要,知其然必知其所以然。正如道与艺一样,道先于艺。老子说:“有道而无艺,尚可求艺;有艺而无道,止于艺”。但实际上应该是一种互补关系,“以道术,以术得道”。参考https://laravel.com/docs/5.6/...https://laravel-china.org/doc...http://www.digpage.com/di.htmlhttp://yansu.org/2014/12/06/我...
