Yii PHP 框架分析 (一)

 

  
  
  
  
  1.   Yii PHP 框架分析 (一) 
  2.  
  3. 作者:wdy 
  4.  
  5. http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html 
  6.  
  7. 基于yii1.0.8的代码分析的。用了一个下午整理的,流水账,感兴趣的凑合着先看,国庆期间推出个整理修改版,然后再完成后两个部分(MVC和Yii的整体结构分析)。 
  8.  
  9. 1. 启动 
  10.  
  11. 网站的唯一入口程序 index.php : 
  12.  
  13.  
  14. $yii=dirname(__FILE__).'/../framework/yii.php'
  15. $config=dirname(__FILE__).'/protected/config/main.php'
  16.  
  17. // remove the following line when in production mode 
  18. defined('YII_DEBUG'or define('YII_DEBUG',true); 
  19.  
  20. require_once($yii); 
  21. Yii::createWebApplication($config)->run(); 
  22.  
  23. 上面的require_once($yii) 引用出了后面要用到的全局类Yii,Yii类是YiiBase类的完全继承: 
  24.  
  25. class Yii extends YiiBase 
  26.  
  27. 系统的全局访问都是通过Yii类(即YiiBase类)来实现的,Yii类的成员和方法都是static类型。 
  28.  
  29. 2. 类加载 
  30.  
  31. Yii利用PHP5提供的spl库来完成类的自动加载。在YiiBase.php 文件结尾处 
  32.  
  33. spl_autoload_register(array('YiiBase','autoload')); 
  34.  
  35. 将YiiBase类的静态方法autoload 注册为类加载器。 PHP autoload 的简单原理就是执行 new 创建对象或通过类名访问静态成员时,系统将类名传递给被注册的类加载器函数,类加载器函数根据类名自行找到对应的类文件并include 。 
  36.  
  37. 下面是YiiBase类的autoload方法: 
  38.  
  39. public static function autoload($className
  40.    // use include so that the error PHP file may appear 
  41.    if(isset(self::$_coreClasses[$className])) 
  42.     include(YII_PATH.self::$_coreClasses[$className]); 
  43.    else if(isset(self::$_classes[$className])) 
  44.     include(self::$_classes[$className]); 
  45.    else 
  46.     include($className.'.php'); 
  47.  
  48. 可以看到YiiBase的静态成员$_coreClasses 数组里预先存放着Yii系统自身用到的类对应的文件路径: 
  49.  
  50. private static $_coreClasses=array
  51.    'CApplication' => '/base/CApplication.php'
  52.    'CBehavior' => '/base/CBehavior.php'
  53.    'CComponent' => '/base/CComponent.php'
  54.    ... 
  55.  
  56. 非 coreClasse 的类注册在YiiBase的$_classes 数组中: 
  57. private static $_classes=array(); 
  58.  
  59. 其他的类需要用Yii::import()讲类路径导入PHP include paths 中,直接 
  60. include($className.'.php'
  61.  
  62. 3. CWebApplication的创建 
  63.  
  64. 回到前面的程序入口的 Yii::createWebApplication($config)->run(); 
  65.  
  66. public static function createWebApplication($config=null) 
  67.    return new CWebApplication($config); 
  68.  
  69. 现在autoload机制开始工作了。 
  70. 当系统 执行 new CWebApplication() 的时候,会自动  
  71. include(YII_PATH.'/base/CApplication.php'
  72.  
  73. 将main.php里的配置信息数组$config传递给 CWebApplication创建出对象,并执行对象的run() 方法启动框架。 
  74.  
  75. CWebApplication类的继承关系 
  76.  
  77. CWebApplication -> CApplication -> CModule -> CComponent 
  78.  
  79. $config先被传递给CApplication的构造函数 
  80.  
  81. public function __construct($config=null) 
  82.    Yii::setApplication($this); 
  83.  
  84.    // set basePath at early as possible to avoid trouble 
  85.    if(is_string($config)) 
  86.     $config=require($config); 
  87.    if(isset($config['basePath'])) 
  88.    { 
  89.     $this->setBasePath($config['basePath']); 
  90.     unset($config['basePath']); 
  91.    } 
  92.    else 
  93.     $this->setBasePath('protected'); 
  94.    Yii::setPathOfAlias('application',$this->getBasePath()); 
  95.    Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME'])); 
  96.  
  97.    $this->preinit(); 
  98.  
  99.    $this->initSystemHandlers(); 
  100.    $this->registerCoreComponents(); 
  101.  
  102.    $this->configure($config); 
  103.    $this->attachBehaviors($this->behaviors); 
  104.    $this->preloadComponents(); 
  105.  
  106.    $this->init(); 
  107.  
  108.  
  109. Yii::setApplication($this); 将自身的实例对象赋给Yii的静态成员$_app,以后可以通过 Yii::app() 来取得。 
  110. 后面一段是设置CApplication 对象的_basePath ,指向 proteced 目录。 
  111.  
  112. Yii::setPathOfAlias('application',$this->getBasePath()); 
  113. Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME'])); 
  114.  
  115. 设置了两个系统路径别名 application 和 webroot,后面再import的时候可以用别名来代替实际的完整路径。别名配置存放在YiiBase的 $_aliases 数组中。 
  116.  
  117. $this->preinit(); 
  118. 预初始化。preinit()是在 CModule 类里定义的,没有任何动作。 
  119.  
  120. $this->initSystemHandlers() 方法内容: 
  121.  
  122. /** 
  123. * Initializes the class autoloader and error handlers. 
  124. */ 
  125. protected function initSystemHandlers() 
  126.    if(YII_ENABLE_EXCEPTION_HANDLER) 
  127.     set_exception_handler(array($this,'handleException')); 
  128.    if(YII_ENABLE_ERROR_HANDLER) 
  129.     set_error_handler(array($this,'handleError'),error_reporting());   
  130.  
  131. 设置系统exception_handler和 error_handler,指向对象自身提供的两个方法。 
  132.  
  133. 4. 注册核心组件 
  134.  
  135. $this->registerCoreComponents(); 
  136. 代码如下: 
  137.  
  138. protected function registerCoreComponents() 
  139.    parent::registerCoreComponents(); 
  140.  
  141.    $components=array
  142.     'urlManager'=>array
  143.      'class'=>'CUrlManager'
  144.     ), 
  145.     'request'=>array
  146.      'class'=>'CHttpRequest'
  147.     ), 
  148.     'session'=>array
  149.      'class'=>'CHttpSession'
  150.     ), 
  151.     'assetManager'=>array
  152.      'class'=>'CAssetManager'
  153.     ), 
  154.     'user'=>array
  155.      'class'=>'CWebUser'
  156.     ), 
  157.     'themeManager'=>array
  158.      'class'=>'CThemeManager'
  159.     ), 
  160.     'authManager'=>array
  161.      'class'=>'CPhpAuthManager'
  162.     ), 
  163.     'clientScript'=>array
  164.      'class'=>'CClientScript'
  165.     ), 
  166.    ); 
  167.  
  168.    $this->setComponents($components); 
  169.  
  170. 注册了几个系统组件(Components)。 
  171. Components 是在 CModule 里定义和管理的,主要包括两个数组 
  172.  
  173. private $_components=array(); 
  174. private $_componentConfig=array(); 
  175.  
  176. 每个 Component 都是 IApplicationComponent接口的实例,Componemt的实例存放在$_components 数组里,相关的配置信息存放在$_componentConfig数组里。配置信息包括Component 的类名和属性设置。 
  177.  
  178. CWebApplication 对象注册了以下几个Component:urlManager, request,session,assetManager,user,themeManager,authManager,clientScript。 CWebApplication的parent 注册了以下几个 Component:coreMessages,db,messages,errorHandler,securityManager,statePersister。 
  179.  
  180. Component 在YiiPHP里是个非常重要的东西,它的特征是可以通过 CModule 的 __get() 和 __set() 方法来访问。 Component 注册的时候并不会创建对象实例,而是在程序里被第一次访问到的时候,由CModule 来负责(实际上就是 Yii::app())创建。 
  181.  
  182. 5. 处理 $config 配置 
  183.  
  184. 继续, $this->configure($config); 
  185. configure() 还是在CModule 里: 
  186.  
  187. public function configure($config
  188.    if(is_array($config)) 
  189.    { 
  190.     foreach($config as $key=>$value
  191.      $this->$key=$value
  192.    } 
  193.  
  194. 实际上是把$config数组里的每一项传给 CModule 的 父类 CComponent __set() 方法。 
  195.  
  196. public function __set($name,$value
  197.    $setter='set'.$name
  198.    if(method_exists($this,$setter)) 
  199.     $this->$setter($value); 
  200.    else if(strncasecmp($name,'on',2)===0  
  201.                && method_exists($this,$name)) 
  202.    { 
  203.     //duplicating getEventHandlers() here for performance 
  204.     $name=strtolower($name); 
  205.     if(!isset($this->_e[$name])) 
  206.      $this->_e[$name]=new CList; 
  207.      $this->_e[$name]->add($value); 
  208.     } 
  209.     else if(method_exists($this,'get'.$name)) 
  210.      throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.'
  211.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  212.     else 
  213.      throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.'
  214.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  215.    } 
  216.  
  217. 我们来看看: 
  218. if(method_exists($this,$setter)) 
  219. 根据这个条件,$config 数组里的basePath, params, modules, import, components 都被传递给相应的 setBasePath(), setParams() 等方法里进行处理。 
  220.  
  221. 6、$config 之 import 
  222.  
  223. 其中 import 被传递给 CModule 的 setImport: 
  224.  
  225. public function setImport($aliases
  226.    foreach($aliases as $alias
  227.     Yii::import($alias); 
  228.  
  229. Yii::import($alias)里的处理: 
  230.  
  231. public static function import($alias,$forceInclude=false) 
  232.    // 先判断$alias是否存在于YiiBase::$_imports[] 中,已存在的直接return, 避免重复import。 
  233.    if(isset(self::$_imports[$alias])) // previously imported 
  234.     return self::$_imports[$alias]; 
  235.  
  236.    // $alias类已定义,记入$_imports[],直接返回 
  237.    if(class_exists($alias,false)) 
  238.     return self::$_imports[$alias]=$alias
  239.  
  240.    // 类似 urlManager 这样的已定义于$_coreClasses[]的类,或不含.的直接类名,记入$_imports[],直接返回 
  241.    if(isset(self::$_coreClasses[$alias]) || ($pos=strrpos($alias,'.'))===false) // a simple class name 
  242.    { 
  243.     self::$_imports[$alias]=$alias
  244.     if($forceInclude
  245.     { 
  246.      if(isset(self::$_coreClasses[$alias])) // a core class 
  247.       require(YII_PATH.self::$_coreClasses[$alias]); 
  248.      else 
  249.       require($alias.'.php'); 
  250.     } 
  251.     return $alias
  252.    } 
  253.  
  254.    // 产生一个变量 $className,为$alias最后一个.后面的部分 
  255.    // 这样的:'x.y.ClassNamer' 
  256.    // $className不等于 '*', 并且ClassNamer类已定义的,      ClassNamer' 记入 $_imports[],直接返回 
  257.    if(($className=(string)substr($alias,$pos+1))!=='*' && class_exists($className,false)) 
  258.     return self::$_imports[$alias]=$className
  259.  
  260.    // 取得 $alias 里真实的路径部分并且路径有效 
  261.    if(($path=self::getPathOfAlias($alias))!==false) 
  262.    { 
  263.     // $className!=='*',$className 记入 $_imports[] 
  264.     if($className!=='*'
  265.     { 
  266.      self::$_imports[$alias]=$className
  267.      if($forceInclude
  268.       require($path.'.php'); 
  269.      else 
  270.       self::$_classes[$className]=$path.'.php'
  271.      return $className
  272.     } 
  273.     // $alias是'system.web.*'这样的已*结尾的路径,将路径加到include_path中 
  274.     else // a directory 
  275.     { 
  276.      set_include_path(get_include_path().PATH_SEPARATOR.$path); 
  277.      return self::$_imports[$alias]=$path
  278.     } 
  279.    } 
  280.    else 
  281.     throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.'
  282.      array('{alias}'=>$alias))); 
  283.  
  284. 7. $config 之 components 
  285.  
  286. $config 数组里的 $components 被传递给CModule 的setComponents($components
  287.  
  288. public function setComponents($components
  289.    foreach($components as $id=>$component
  290.    { 
  291.     if($component instanceof IApplicationComponent) 
  292.      $this->setComponent($id,$component); 
  293.     else if(isset($this->_componentConfig[$id])) 
  294.      $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component); 
  295.     else 
  296.      $this->_componentConfig[$id]=$component
  297.    } 
  298.  
  299. $componen是IApplicationComponen的实例的时候,直接赋值: 
  300. $this->setComponent($id,$component), 
  301.  
  302. public function setComponent($id,$component
  303.    $this->_components[$id]=$component
  304.    if(!$component->getIsInitialized()) 
  305.     $component->init(); 
  306.  
  307. 如果$id已存在于_componentConfig[]中(前面注册的 coreComponent),将$component 属性加进入。 
  308. 其他的component将component属性存入_componentConfig[]中。 
  309.  
  310. 8. $config 之 params 
  311.  
  312. 这个很简单 
  313. public function setParams($value
  314.    $params=$this->getParams(); 
  315.    foreach($value as $k=>$v
  316.     $params->add($k,$v); 
  317.  
  318. configure 完毕! 
  319.  
  320. 9. attachBehaviors 
  321.  
  322. $this->attachBehaviors($this->behaviors); 
  323. 空的,没动作 
  324.  
  325. 预创建组件对象 
  326. $this->preloadComponents(); 
  327.  
  328. protected function preloadComponents() 
  329.    foreach($this->preload as $id
  330.     $this->getComponent($id); 
  331.  
  332. getComponent() 判断_components[] 数组里是否有 $id的实例,如果没有,就根据_componentConfig[$id]里的配置来创建组件对象,调用组件的init()方法,然后存入 _components[$id]中。 
  333.  
  334. 10. init() 
  335.  
  336. $this->init(); 
  337.  
  338. 函数内:$this->getRequest(); 
  339. 创建了Reques 组件并初始化。 
  340.  
  341. 11. run() 
  342.  
  343. public function run() 
  344.    $this->onBeginRequest(new CEvent($this)); 
  345.    $this->processRequest(); 
  346.    $this->onEndRequest(new CEvent($this)); 

 

你可能感兴趣的:(PHP,职场,yii,休闲,框架分析)