前言对于PHP的框架,不管是yii、symfony还是laravel,大家都有所涉猎。对于框架中存放的资源包vendor文件夹和入口文件(index.php或者app.php),大家也是天天遇到。但是你真的熟悉这些文件/文件夹吗?一个完整的项目是如何从一个纯框架开发出来的?每个部分在构建这座建筑中扮演什么角色?1.依赖注入依赖注入不是sql注入o( ̄ヘ ̄o#)1.1说说依赖注入和反射类的关系说到依赖注入DI,90%的文章都会提到一个叫做反射的东西,很多评委(包括我)认为两者本身就是一个,但实际上两者是两个不相干的概念。Reflection是反射类,而是依赖注入。DI是一种设计模式。反射类和设计模式怎么能属于同一个东西呢?所以他们的关系是依赖关系——依赖注入都是依赖反射类的设计模式。1.2什么是反射写一个非常简单的类A,它只有一个对象$a和一个方法a()。A类{公共$a;公共函数a(){echo__FUNCTION__;之后我们开始调用反射类Reflection$A=newA();$reflect=newReflectionObject($A);$props=$reflect->getProperties();//获取这个类的所有对象...我们可以通过反射知道这个类所有我们想知道的内容,就像获取一个类的所有细节像x-rays。然而,当我上完反思课,我又陷入了迷茫。设计这个反射类的目的是什么?想知道原类有多少方法/对象,只看原类不行吗?何必呢?这与依赖注入有什么关系?别着急,我们先说点别的,回过头来看。1.3都是依赖怎么办?现在有这样一个场景,我想实例化B,但是B依赖于A,这个应该怎么办呢?它可以非常简单地解决,如下所示。ClassA{//dosth.}ClassB{publicfunction__construct(A$a){//ClassB依赖于A}}$a=newA();$b=newB($a);简单吧?两招就能解决,没什么难的。但是如果现在有这样一种情况:从A到Z一共有26个类,B类依赖A,C类依赖B,以此类推。那么如果我要实例化Z类,需要做什么呢?你还在用上图的方法吗?实例化Z不是要写26行吗?那么有没有办法直接实例化B,然后自动将其他类全部加载进去呢?真的有。需要用到上面刚刚提到的反射类。newInstanceArgs($paramArr);}/***获取类的方法参数,只获取类型化参数。*通过递归方法*@param[type]$className[类名]*@param[type]$methodsName[方法名]*@return[mixed]*/protectedstaticfunctiongetMethodParams($className,$methodsName='__construct'){//通过反射获取类$class=newReflectionClass($className);$paramArr=[];//记录参数,以及参数类型//判断类是否有构造函数if($class->hasMethod($methodsName)){//获取构造函数$construct=$class->getMethod($methodsName);//判断构造函数是否有参数$params=$construct->getParameters();if(count($params)>0){//判断参数类型foreach($paramsas$key=>$param){if($paramClass=$param->getClass()){//获取参数类型名称$paramClassName=$paramClass->getName();/***获取参数类型*这里使用递归*/$args=self::getMethodParams($paramClassName);$paramArr[]=(newReflectionClass($paramClass->getName()))->newInstanceArgs($args);}}}}返回$paramArr;}/***执行类方法*@param[type]$className[类名]*@param[类型]$methodName[方法名]*@param[类型]$params[额外参数]*@return[类型][说明]*/publicstaticfunctionmake($className,$methodName,$params=[]){//获取类的一个实例$instance=self::getInstance($className);//获取该方法需要的依赖注入$paramArr=self::getMethodParams($className,$methodName);返回$instance->{$methodName}(...array_merge($paramArr,$params));}}classA{//...}classB{publicfunction__construct(A$a){//...}}classC{publicfunction__construct(B$b){//...}}$cObj=Ioc::getInstance('C');让我们仔细看看Ioc类的getMethodParams方法。首先说一下大概的情况:这个方法是通过递归遍历加载的类,实现所有的依赖类。加载的目的。该方法首先实例化Reflection,然后使用getMethod方法获取加载类的__construct方法,然后查看这个__construct中是否有其他类作为参数。如果是,则递归重复上述过程,直到没有依赖为止。上面是一个使用反射进行依赖注入的简单例子。虽然在实际框架中会比上面的例子复杂一些,但还是一样的。例如,在很多框架中,都会用到一个叫做容器的概念——使用$this->getContainer()->get(YOUR_CLASS_NAME)来获取类。这个容器是什么高科技东西?别急,毕竟还是依赖注入。稍后我们将深入了解symfony容器是如何设计的。
