As the first show of source code analysis, I chose yii (pronounced waiyiyi instead of wai Love love); I won’t say much about its praise, and will go straight to the point. Prepare materials first. It is recommended to download the latest version of yii source code package (1.1.15) directly from the official website.
There is one of the simplest applications in the demos - helloworld. It uses the yii framework to output a sentence: "hello world";
I will start from it, analyze the components that the framework goes through to execute a minimum process, and briefly analyze its running process.
Start reading from the single entry document. (Source code is generally analyzed starting from the calling point)
Index.php->
// include Yii bootstrap file
//Introduce startup file
require_once(dirname(__FILE__).'/../../framework/yii.php');
yii.php ->
//YiiBase is a helper class serving common framework functionalities.
//YiiBase is a helper class that serves the entire framework. Many important constants are defined here
require(dirname(__FILE__).'/YiiBase.php');
//Register automatic loading class
spl_autoload_register(array('YiiBase','autoload'));
//Import interface class
require(YII_PATH.'/base/interfaces.php');
//Start the application
Yii::createWebApplication()->run();
The code seems to end here, and the content of the page is also displayed, but we have no idea what the framework does. So we need to break this step down and expose the details inside.
Yii::createWebApplication()->run() can be divided into three parts
What is Yii?
You can find this line of code from yii.php class Yii extends YiiBase
indicates that it is the inherited class of YiiBase, and the author's extension is left blank, so Yii is just a reference to YiiBase. Therefore, the static method createWebApplication also has to be found in YiiBase.
In YiiBase.php, it is easy to find this method:
public static function createWebApplication($config=null)
{
return self::createApplication('CWebApplication',$config);
}
It passes the task to createApplication:
public static function createApplication($class,$config=null)
{
return new $class($config);
}
Taken together, createWebApplication () is return new CWebApplication($config);
Where is this CWebApplication class? How was it introduced?
With a series of questions, I returned to YiiBase.php
There is a very long array defined there, you can find:
'CWebApplication' => '/web/CWebApplication.php', this is a member of the autoloading class
For how it implements automatic loading, you can view the relevant documents of spl_autoload_register, but here is an extraneous detail.
Let’s continue to dig deeper into CWebApplication. Open the file /web/CWebApplication.php.
As mentioned earlier, return new CWebApplication($config); according to my experience, when using new, there is usually a constructor, but I did not find its constructor. It must be in its parent. class, so I looked up, and I discovered class CWebApplication extends CApplication. It was like digging for a loach, I had to follow the clues and find it bit by bit, and I had to be patient.
There is indeed a constructor in CApplication, the code is as follows:
<span><span>public</span> <span>function</span> __construct(<span>$config</span>=<span>null</span><span>) { Yii</span>::setApplication(<span>$this</span><span>); </span><span>//</span><span> set basePath at early as possible to avoid trouble</span> <span>if</span>(<span>is_string</span>(<span>$config</span><span>)) </span><span>$config</span>=<span>require</span>(<span>$config</span><span>); </span><span>if</span>(<span>isset</span>(<span>$config</span>['basePath'<span>])) { </span><span>$this</span>->setBasePath(<span>$config</span>['basePath'<span>]); </span><span>unset</span>(<span>$config</span>['basePath'<span>]); } </span><span>else</span> <span>$this</span>->setBasePath('protected'<span>); Yii</span>::setPathOfAlias('application',<span>$this</span>-><span>getBasePath()); Yii</span>::setPathOfAlias('webroot',<span>dirname</span>(<span>$_SERVER</span>['SCRIPT_FILENAME'<span>])); </span><span>if</span>(<span>isset</span>(<span>$config</span>['extensionPath'<span>])) { </span><span>$this</span>->setExtensionPath(<span>$config</span>['extensionPath'<span>]); </span><span>unset</span>(<span>$config</span>['extensionPath'<span>]); } </span><span>else</span><span> Yii</span>::setPathOfAlias('ext',<span>$this</span>->getBasePath().DIRECTORY_SEPARATOR.'extensions'<span>); </span><span>if</span>(<span>isset</span>(<span>$config</span>['aliases'<span>])) { </span><span>$this</span>->setAliases(<span>$config</span>['aliases'<span>]); </span><span>unset</span>(<span>$config</span>['aliases'<span>]); }</span></span>
//The above can be regarded as initialization, setting class references, aliases, paths, etc.
<$> $ this- & gt; preinit (); // No use is used for the time being, it is estimated that it is left to the later expansion
$this->initSystemHandlers();//设置错误处理
$this->registerCoreComponents();//注册核心组件
$this->configure($config); //通过配置文件扩展类的属性,为空的时候什么也不做
$this->attachBehaviors($this->behaviors);
$this->preloadComponents();
$this->init();
}
$this下面的某些方法,在当前类是找不到的,因为它们可能是来自父类,最简单的方法就是ctrl+f搜索一下,没有就去类的声明处查看:
abstract class CApplication extends CModule
显然要进入CModule,如果此时还找不到想要方法,那么继续上一过程:
abstract class CModule extends CComponent
直到class CComponent
说明这就是当前这些家伙的老巢了。从代码的中注释中也可以看到:
CComponent is the base class for all components
说明我的想法是正确的,没错,它就是基类。
透过代码,我们可以发现,我们当前的应用,因为很多参数是空,所以很多逻辑都是直接跳过的。
看到这,$this的内容也大致明了啦。我们再回头看看
return new CWebApplication($config)->run();
通过前面的层层分析,此时的run方法也很好找了。就在CApplication 里边:
<span><span>public</span> <span>function</span><span> run() { </span><span>if</span>(<span>$this</span>->hasEventHandler('onBeginRequest'<span>)){ </span><span>$this</span>->onBeginRequest(<span>new</span> CEvent(<span>$this</span><span>)); } </span><span>register_shutdown_function</span>(<span>array</span>(<span>$this</span>,'end'),0,<span>false</span><span>); </span><span>$this</span>-><span>processRequest(); </span><span>if</span>(<span>$this</span>->hasEventHandler('onEndRequest'<span>)){ </span><span>$this</span>->onEndRequest(<span>new</span> CEvent(<span>$this</span><span>)); } }</span></span>
重点放在:$this->processRequest(); 因为前面和后面部分都是注册事件相关的,当前条件下执行不到。
<span><span>abstract</span> <span>public</span> <span>function</span> processRequest(); 这个方法在当前类中是抽象的,所以肯定在它的子类中实现了。回去找CWebApplication: <span>public</span> <span>function</span><span> processRequest() { </span><span>if</span>(<span>is_array</span>(<span>$this</span>->catchAllRequest) && <span>isset</span>(<span>$this</span>->catchAllRequest[0<span>])) { </span><span>$route</span>=<span>$this</span>->catchAllRequest[0<span>]; </span><span>foreach</span>(<span>array_splice</span>(<span>$this</span>->catchAllRequest,1) <span>as</span> <span>$name</span>=><span>$value</span><span>) </span><span>$_GET</span>[<span>$name</span>]=<span>$value</span><span>; } </span><span>else</span> <span>$route</span>=<span>$this</span>->getUrlManager()->parseUrl(<span>$this</span>-><span>getRequest()); </span><span>$this</span>->runController(<span>$route</span><span>); }</span></span>
注意重点在$this->runController($route);
<span><span>public</span> <span>function</span> runController(<span>$route</span><span>) { </span><span>if</span>((<span>$ca</span>=<span>$this</span>->createController(<span>$route</span>))!==<span>null</span><span>) { </span><span>list</span>(<span>$controller</span>,<span>$actionID</span>)=<span>$ca</span><span>; </span><span>$oldController</span>=<span>$this</span>-><span>_controller; </span><span>$this</span>->_controller=<span>$controller</span><span>; </span><span>$controller</span>-><span>init(); </span><span>$controller</span>->run(<span>$actionID</span><span>); </span><span>$this</span>->_controller=<span>$oldController</span><span>; } </span><span>else</span> <span>throw</span> <span>new</span> CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', <span>array</span>('{route}'=><span>$route</span>===''?<span>$this</span>->defaultController:<span>$route</span><span>))); }</span></span>
我们要注意的代码只有两行:
$controller->init();
$controller->run($actionID);
这里的$controller可以能过查看createController得知,就是默认的控制器Sitecontroller.php
而Action则是index,你问我是怎么看出来的?哈哈,我在猜不出来的地方echo或var_dump一下不就可以了吗?这么简单的逻辑,还轮不到xdebug 这样的神器出场。
显然,init什么也没有做,看看run做了什么
Sitecontroller中没有run方法,又要去它的父类中查找。
class SiteController extends CController
在CController中有这个方法:
<span><span>public</span> <span>function</span> run(<span>$actionID</span><span>) { </span><span>if</span>((<span>$action</span>=<span>$this</span>->createAction(<span>$actionID</span>))!==<span>null</span><span>) { </span><span>if</span>((<span>$parent</span>=<span>$this</span>->getModule())===<span>null</span><span>){ </span><span>$parent</span>=Yii::<span>app(); } </span><span>if</span>(<span>$parent</span>->beforeControllerAction(<span>$this</span>,<span>$action</span><span>)) { </span><span>$this</span>->runActionWithFilters(<span>$action</span>,<span>$this</span>-><span>filters()); </span><span>$parent</span>->afterControllerAction(<span>$this</span>,<span>$action</span><span>); } } </span><span>else</span> <span>$this</span>->missingAction(<span>$actionID</span><span>); } </span></span>
能过查看$this->createAction($actionID),得到return new CInlineAction($this,$actionID);
我们呆会再看这个CInlineAction,先看$this->runActionWithFilters($action,$this->filters());
<span><span>public</span> <span>function</span> runActionWithFilters(<span>$action</span>,<span>$filters</span><span>) { </span><span>if</span>(<span>empty</span>(<span>$filters</span><span>)){ </span><span>$this</span>->runAction(<span>$action</span><span>); } </span><span>else</span><span> { </span><span>$priorAction</span>=<span>$this</span>-><span>_action; </span><span>$this</span>->_action=<span>$action</span><span>; CFilterChain</span>::create(<span>$this</span>,<span>$action</span>,<span>$filters</span>)-><span>run(); </span><span>$this</span>->_action=<span>$priorAction</span><span>; } }</span></span>
显然$filters是空的,所以执行第一个表达式$this->runAction($action);
<span><span>public</span> <span>function</span> runAction(<span>$action</span><span>) { </span><span>$priorAction</span>=<span>$this</span>-><span>_action; </span><span>$this</span>->_action=<span>$action</span><span>; </span><span>if</span>(<span>$this</span>->beforeAction(<span>$action</span><span>)) { </span><span>if</span>(<span>$action</span>->runWithParams(<span>$this</span>->getActionParams())===<span>false</span><span>){ </span><span>$this</span>->invalidActionParams(<span>$action</span><span>); } </span><span>else</span><span>{ </span><span>$this</span>->afterAction(<span>$action</span><span>); } } </span><span>$this</span>->_action=<span>$priorAction</span><span>; }</span></span>
这段代码的重点是 $action->runWithParams($this->getActionParams())这一句;
这里的$action就是$this->createAction($actionID)返回的结果,而它的结果就是
return new CInlineAction($this,$actionID);
CInlineAction.php
是时候查看CInlineAction了;
<span> <span>public</span> <span>function</span> runWithParams(<span>$params</span><span>) { </span><span>$methodName</span>='action'.<span>$this</span>-><span>getId(); </span><span>$controller</span>=<span>$this</span>-><span>getController(); </span><span>$method</span>=<span>new</span> ReflectionMethod(<span>$controller</span>, <span>$methodName</span><span>); </span><span>if</span>(<span>$method</span>->getNumberOfParameters()>0<span>) </span><span>return</span> <span>$this</span>->runWithParamsInternal(<span>$controller</span>, <span>$method</span>, <span>$params</span><span>); </span><span>else</span> <span>return</span> <span>$controller</span>-><span>$methodName</span><span>(); }</span></span>
哇哦,好高级,居然还用了反射,不过我喜欢!
不过呢,打印$method发现:
object(ReflectionMethod)#6 (2) { |
|
["name"]=> |
|
string(11) "actionIndex" |
|
["class"]=> |
|
string(14) "SiteController" |
|
} |
没有参数,所以此处代码相当于是执行了SiteController->actionIndex();
在class SiteController extends CController中可以看到actionIndex 的定义
<span> <span>public</span> <span>function</span><span> actionIndex() { </span><span>echo</span> 'Hello World'<span>; }</span></span>
于是就看到屏幕上那一句Hello World ,整个程序也就跑完了。也许有人要问了,为什么输出一句话还这么复杂,不是脱了裤子打屁吗? (请允许我的粗俗);
如果是这么简单的需求,当然不可能这么干。举这个例子,只是说明yii的基础流程,为下面的复杂应用做一个过渡。
Yii作为一个优秀的oop框架,这个例子只是介绍了它的继承,接口,mvc中的vc特性,关于数据模型,我将在后面的分析中陆续给出。最终的目标,是利用yii框架简化我们的开发过程。
好了,今天的分析就在到了,如果有什么不妥的,请留言,如果觉得有帮助,请顺手点个推荐!