十三)CodeIgniter源码分析之Loader.php

   1 <?php  if ( ! defined(‘BASEPATH‘)) exit(‘No direct script access allowed‘);
   2 /**
   3  * Loader Class
   4  *
   5  * Loader组件在CI里面也是一个很重要的组件,功能也比较明了。
   6  * 如果已经阅读过Controller组件,会发现Controller组件的代码也只有十来行,但它却可以做很多事,一定程度上
   7  * 要归功于Loader组件这个好助手或者好基友。
   8  * 不过Loader组件的代码真的不少,主要以常用的几个方法以主线来探讨:model(),view(),library(),helper();
   9  */
  10 class CI_Loader {
  11
  12  // All these are set automatically. Don‘t mess with them.
  13  /**
  14   * Nesting level of the output buffering mechanism
  15   */
  16  protected $_ci_ob_level;
  17  /**
  18   * List of paths to load views from
  19   */
  20  protected $_ci_view_paths  = array();
  21  /**
  22   * List of paths to load libraries from
  23   */
  24  protected $_ci_library_paths = array();
  25  /**
  26   * List of paths to load models from
  27   */
  28  protected $_ci_model_paths  = array();
  29  /**
  30   * List of paths to load helpers from
  31   */
  32  protected $_ci_helper_paths  = array();
  33  /**
  34   * List of loaded base classes
  35   */
  36  protected $_base_classes  = array(); // Set by the controller class
  37  /**
  38   * List of cached variables
  39   */
  40  protected $_ci_cached_vars  = array();
  41  /**
  42   * List of loaded classes
  43   */
  44  protected $_ci_classes   = array();
  45  /**
  46   * List of loaded files
  47   */
  48  protected $_ci_loaded_files  = array();
  49  /**
  50   * List of loaded models
  51   */
  52  protected $_ci_models   = array();
  53  /**
  54   * List of loaded helpers
  55   */
  56  protected $_ci_helpers   = array();
  57  /**
  58   * List of class name mappings
  59   */
  60  protected $_ci_varmap   = array(‘unit_test‘ => ‘unit‘,
  61            ‘user_agent‘ => ‘agent‘);
  62
  63  /**
  64   * Constructor
  65   */
  66  public function __construct()
  67  {
  68   $this->_ci_ob_level  = ob_get_level();
  69   $this->_ci_library_paths = array(APPPATH, BASEPATH);
  70   $this->_ci_helper_paths = array(APPPATH, BASEPATH);
  71   $this->_ci_model_paths = array(APPPATH);
  72   $this->_ci_view_paths = array(APPPATH.‘views/‘ => TRUE);
  73
  74   log_message(‘debug‘, "Loader Class Initialized");
  75  }
  76
  77  // --------------------------------------------------------------------
  78
  79  /**
  80   * Initialize the Loader
  81   */
  82  public function initialize()
  83  {
  84   $this->_ci_classes = array();
  85   $this->_ci_loaded_files = array();
  86   $this->_ci_models = array();
  87
  88   //这个is_loaded方法就是在core/Common.php中定义的全局函数,在Loader组件还没有加载之前,由它来负责记录
  89   //哪些核心类已经加载过,现在Loader组件要加载了,就把信息交给Loader组件,保存在Loader::$_base_classes中。
  90   $this->_base_classes =& is_loaded();
  91
  92   //自动加载,加载项是你在config/autoload.php中设置的。
  93   $this->_ci_autoloader();
  94
  95   return $this;
  96  }
  97
  98  // --------------------------------------------------------------------
  99
 100  /**
 101   * Is Loaded
 102   */
 103  public function is_loaded($class)
 104  {
 105   if (isset($this->_ci_classes[$class]))
 106   {
 107    return $this->_ci_classes[$class];
 108   }
 109
 110   return FALSE;
 111  }
 112
 113  // --------------------------------------------------------------------
 114
 115  /**
 116   * Class Loader
 117   * $library为相应的类名,$params为实例化此类的时候可能要用到的参数,$object_name为给这个类的实例自义定一个名字。
 118   */
 119  public function library($library = ‘‘, $params = NULL, $object_name = NULL)
 120  {
 121   //如果是通过数组加载多个,把它拆开再调用本方法,其实它可以递归调用多维数组,不过没有这个必要。
 122   if (is_array($library))
 123   {
 124    foreach ($library as $class)
 125    {
 126     $this->library($class, $params);
 127    }
 128
 129    return;
 130   }
 131
 132   //接下来两个if都是关于合法性的判断。
 133   if ($library == ‘‘ OR isset($this->_base_classes[$library]))
 134   {
 135    return FALSE;
 136   }
 137
 138   if ( ! is_null($params) && ! is_array($params))
 139   {
 140    $params = NULL;
 141   }
 142
 143   //真正把类加载进来的是下面这个方法。
 144   $this->_ci_load_class($library, $params, $object_name);
 145  }
 146
 147  // --------------------------------------------------------------------
 148
 149  /**
 150   * Model Loader
 151   */
 152  public function model($model, $name = ‘‘, $db_conn = FALSE)
 153  {
 154   //可以以数组形式同时加载多个$model
 155   if (is_array($model))
 156   {
 157    foreach ($model as $babe)
 158    {
 159     $this->model($babe);
 160    }
 161    return;
 162   }
 163
 164   if ($model == ‘‘)
 165   {
 166    return;
 167   }
 168
 169   $path = ‘‘;
 170
 171   //判断是否包含目录信息
 172   if (($last_slash = strrpos($model, ‘/‘)) !== FALSE)
 173   {
 174    $path = substr($model, 0, $last_slash + 1);
 175
 176    $model = substr($model, $last_slash + 1);
 177   }
 178
 179   //如果没有给当前model定义名字,则以$model本身作为名字。
 180   if ($name == ‘‘)
 181   {
 182    $name = $model;
 183   }
 184
 185   //如果已经加载过此model,直接退出本函数。
 186   if (in_array($name, $this->_ci_models, TRUE))
 187   {
 188    return;
 189   }
 190
 191   $CI =& get_instance();
 192   //如果加载的model名字与之前加载过的类有冲突,则报错。
 193   if (isset($CI->$name))
 194   {
 195    show_error(‘The model name you are loading is the name of a resource that is already being used: ‘.$name);
 196   }
 197
 198   //model文件必段是全小写。
 199   $model = strtolower($model);
 200
 201   foreach ($this->_ci_model_paths as $mod_path)
 202   {
 203    if ( ! file_exists($mod_path.‘models/‘.$path.$model.‘.php‘))
 204    {
 205     continue;
 206    }
 207
 208    //如果要求同时连接数据库。则调用Loader::database()方法加载数据库类。
 209    if ($db_conn !== FALSE AND ! class_exists(‘CI_DB‘))
 210    {
 211     if ($db_conn === TRUE)
 212     {
 213      $db_conn = ‘‘;
 214     }
 215
 216     $CI->load->database($db_conn, FALSE, TRUE);
 217    }
 218
 219    //加载父类model。
 220    if ( ! class_exists(‘CI_Model‘))
 221    {
 222     load_class(‘Model‘, ‘core‘);
 223    }
 224
 225    //引入当前model
 226    require_once($mod_path.‘models/‘.$path.$model.‘.php‘);
 227
 228    //把文件名的第一个字母大写作为类名,规定的命名规范。
 229    $model = ucfirst($model);
 230
 231    $CI->$name = new $model();
 232
 233    //保存在Loader::_ci_models中,以后可以用它来判断某个model是否已经加载过。
 234    $this->_ci_models[] = $name;
 235    return;
 236   }
 237
 238   show_error(‘Unable to locate the model you have specified: ‘.$model);
 239  }
 240
 241  // --------------------------------------------------------------------
 242
 243  /**
 244   * Database Loader
 245   */
 246  public function database($params = ‘‘, $return = FALSE, $active_record = NULL)
 247  {
 248   $CI =& get_instance();
 249   if (class_exists(‘CI_DB‘) AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
 250   {
 251    return FALSE;
 252   }
 253
 254   require_once(BASEPATH.‘database/DB.php‘);
 255
 256   if ($return === TRUE)
 257   {
 258    return DB($params, $active_record);
 259   }
 260   $CI->db = ‘‘;
 261
 262   $CI->db =& DB($params, $active_record);
 263  }
 264
 265  // --------------------------------------------------------------------
 266
 267  /**
 268   * Load the Utilities Class
 269   */
 270  public function dbutil()
 271  {
 272   if ( ! class_exists(‘CI_DB‘))
 273   {
 274    $this->database();
 275   }
 276
 277   $CI =& get_instance();
 278
 279   // for backwards compatibility, load dbforge so we can extend dbutils off it
 280   // this use is deprecated and strongly discouraged
 281   $CI->load->dbforge();
 282
 283   require_once(BASEPATH.‘database/DB_utility.php‘);
 284   require_once(BASEPATH.‘database/drivers/‘.$CI->db->dbdriver.‘/‘.$CI->db->dbdriver.‘_utility.php‘);
 285   $class = ‘CI_DB_‘.$CI->db->dbdriver.‘_utility‘;
 286
 287   $CI->dbutil = new $class();
 288  }
 289
 290  // --------------------------------------------------------------------
 291
 292  /**
 293   * Load the Database Forge Class
 294   */
 295  public function dbforge()
 296  {
 297   if ( ! class_exists(‘CI_DB‘))
 298   {
 299    $this->database();
 300   }
 301
 302   $CI =& get_instance();
 303
 304   require_once(BASEPATH.‘database/DB_forge.php‘);
 305   require_once(BASEPATH.‘database/drivers/‘.$CI->db->dbdriver.‘/‘.$CI->db->dbdriver.‘_forge.php‘);
 306   $class = ‘CI_DB_‘.$CI->db->dbdriver.‘_forge‘;
 307
 308   $CI->dbforge = new $class();
 309  }
 310
 311  // --------------------------------------------------------------------
 312
 313  /**
 314   * Load View
 315   *
 316   * Loader::view();方法可以和Loader::file()方法一并来阅读,实质上它们都是调用了Loader::_ci_load();方法。
 317   */
 318  public function view($view, $vars = array(), $return = FALSE)
 319  {
 320   return $this->_ci_load(array(‘_ci_view‘ => $view, ‘_ci_vars‘ => $this->_ci_object_to_array($vars), ‘_ci_return‘ => $return));
 321  }
 322
 323  // --------------------------------------------------------------------
 324
 325  /**
 326   * Load File
 327   */
 328  public function file($path, $return = FALSE)
 329  {
 330   return $this->_ci_load(array(‘_ci_path‘ => $path, ‘_ci_return‘ => $return));
 331  }
 332
 333  // --------------------------------------------------------------------
 334
 335  /**
 336   * Set Variables
 337   */
 338  public function vars($vars = array(), $val = ‘‘)
 339  {
 340   if ($val != ‘‘ AND is_string($vars))
 341   {
 342    $vars = array($vars => $val);
 343   }
 344
 345   $vars = $this->_ci_object_to_array($vars);
 346
 347   if (is_array($vars) AND count($vars) > 0)
 348   {
 349    foreach ($vars as $key => $val)
 350    {
 351     $this->_ci_cached_vars[$key] = $val;
 352    }
 353   }
 354  }
 355
 356  // --------------------------------------------------------------------
 357
 358  /**
 359   * Get Variable
 360   */
 361  public function get_var($key)
 362  {
 363   return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
 364  }
 365
 366  // --------------------------------------------------------------------
 367
 368  /**
 369   * Load Helper
 370   */
 371  public function helper($helpers = array())
 372  {
 373   //Loader::_ci_prep_filename()方法只是处理文件名,以返回正确的数组而已。
 374   //默认helper的文件名是以_helper为后缀,所以参数可以不用写_helper后缀,当然写也不会出错,因为
 375   //Loader::_ci_prep_filename()会帮你处理掉。
 376   foreach ($this->_ci_prep_filename($helpers, ‘_helper‘) as $helper)
 377   {
 378    //如果已经加载过此helper,则跳过。
 379    if (isset($this->_ci_helpers[$helper]))
 380    {
 381     continue;
 382    }
 383
 384    //helper的扩展。并非只有类可以扩展,函数也提供了扩展。注意这里是在APPPATH下。
 385    $ext_helper = APPPATH.‘helpers/‘.config_item(‘subclass_prefix‘).$helper.‘.php‘;
 386    //如果存在对些helper的扩展。
 387    if (file_exists($ext_helper))
 388    {
 389     //先引入CI自带的helper,再引入我们写的扩展。
 390     $base_helper = BASEPATH.‘helpers/‘.$helper.‘.php‘;
 391
 392     if ( ! file_exists($base_helper))
 393     {
 394      show_error(‘Unable to load the requested file: helpers/‘.$helper.‘.php‘);
 395     }
 396
 397     include_once($ext_helper);
 398     include_once($base_helper);
 399
 400     $this->_ci_helpers[$helper] = TRUE;
 401     log_message(‘debug‘, ‘Helper loaded: ‘.$helper);
 402     continue;//继续下一个helper的引入。
 403    }
 404
 405    //如果没有扩展的话,则分别从APPPATH和BASEPATH,即应用目录和系统目录下找到相应的helper。
 406    //Loader::_ci_helper_paths默认是APPPATH和BASEPATH两个目录。如果你要再添加新的目录路径,可以
 407    //通过Loader::add_package_path()方法设置(model,library等同理)
 408    foreach ($this->_ci_helper_paths as $path)
 409    {
 410     if (file_exists($path.‘helpers/‘.$helper.‘.php‘))
 411     {
 412      include_once($path.‘helpers/‘.$helper.‘.php‘);
 413
 414      $this->_ci_helpers[$helper] = TRUE;
 415      log_message(‘debug‘, ‘Helper loaded: ‘.$helper);
 416      break;
 417     }
 418    }
 419
 420    if ( ! isset($this->_ci_helpers[$helper]))
 421    {
 422     show_error(‘Unable to load the requested file: helpers/‘.$helper.‘.php‘);
 423    }
 424   }
 425  }
 426
 427  // --------------------------------------------------------------------
 428
 429  /**
 430   * Load Helpers
 431   */
 432  public function helpers($helpers = array())
 433  {
 434   $this->helper($helpers);
 435  }
 436
 437  // --------------------------------------------------------------------
 438
 439  /**
 440   * Loads a language file
 441   */
 442  public function language($file = array(), $lang = ‘‘)
 443  {
 444   $CI =& get_instance();
 445
 446   if ( ! is_array($file))
 447   {
 448    $file = array($file);
 449   }
 450
 451   foreach ($file as $langfile)
 452   {
 453    $CI->lang->load($langfile, $lang);
 454   }
 455  }
 456
 457  // --------------------------------------------------------------------
 458
 459  /**
 460   * Loads a config file
 461   */
 462  //这里的config方法,实质是完完全全调用Config组件的load方法而已。
 463  public function config($file = ‘‘, $use_sections = FALSE, $fail_gracefully = FALSE)
 464  {
 465   $CI =& get_instance();
 466   $CI->config->load($file, $use_sections, $fail_gracefully);
 467  }
 468
 469  // --------------------------------------------------------------------
 470
 471  /**
 472   * Driver
 473   */
 474  public function driver($library = ‘‘, $params = NULL, $object_name = NULL)
 475  {
 476   if ( ! class_exists(‘CI_Driver_Library‘))
 477   {
 478    require BASEPATH.‘libraries/Driver.php‘;
 479   }
 480
 481   if ($library == ‘‘)
 482   {
 483    return FALSE;
 484   }
 485
 486   if ( ! strpos($library, ‘/‘))
 487   {
 488    $library = ucfirst($library).‘/‘.$library;
 489   }
 490
 491   return $this->library($library, $params, $object_name);
 492  }
 493
 494  // --------------------------------------------------------------------
 495
 496  /**
 497   * Add Package Path
 498   */
 499  public function add_package_path($path, $view_cascade=TRUE)
 500  {
 501   $path = rtrim($path, ‘/‘).‘/‘;
 502
 503   array_unshift($this->_ci_library_paths, $path);
 504   array_unshift($this->_ci_model_paths, $path);
 505   array_unshift($this->_ci_helper_paths, $path);
 506
 507   $this->_ci_view_paths = array($path.‘views/‘ => $view_cascade) + $this->_ci_view_paths;
 508
 509   $config =& $this->_ci_get_component(‘config‘);
 510   array_unshift($config->_config_paths, $path);
 511  }
 512
 513  // --------------------------------------------------------------------
 514
 515  /**
 516   * Get Package Paths
 517   *
 518   */
 519  public function get_package_paths($include_base = FALSE)
 520  {
 521   return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
 522  }
 523
 524  // --------------------------------------------------------------------
 525
 526  /**
 527   * Remove Package Path
 528   */
 529  public function remove_package_path($path = ‘‘, $remove_config_path = TRUE)
 530  {
 531   $config =& $this->_ci_get_component(‘config‘);
 532
 533   if ($path == ‘‘)
 534   {
 535    $void = array_shift($this->_ci_library_paths);
 536    $void = array_shift($this->_ci_model_paths);
 537    $void = array_shift($this->_ci_helper_paths);
 538    $void = array_shift($this->_ci_view_paths);
 539    $void = array_shift($config->_config_paths);
 540   }
 541   else
 542   {
 543    $path = rtrim($path, ‘/‘).‘/‘;
 544    foreach (array(‘_ci_library_paths‘, ‘_ci_model_paths‘, ‘_ci_helper_paths‘) as $var)
 545    {
 546     if (($key = array_search($path, $this->{$var})) !== FALSE)
 547     {
 548      unset($this->{$var}[$key]);
 549     }
 550    }
 551
 552    if (isset($this->_ci_view_paths[$path.‘views/‘]))
 553    {
 554     unset($this->_ci_view_paths[$path.‘views/‘]);
 555    }
 556
 557    if (($key = array_search($path, $config->_config_paths)) !== FALSE)
 558    {
 559     unset($config->_config_paths[$key]);
 560    }
 561   }
 562
 563   $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
 564   $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
 565   $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
 566   $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.‘views/‘ => TRUE));
 567   $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
 568  }
 569
 570  // --------------------------------------------------------------------
 571
 572  /**
 573   * Loader
 574   */
 575  protected function _ci_load($_ci_data)
 576  {
 577   //这里相当于把数组里面的元素拆开成变量。
 578   foreach (array(‘_ci_view‘, ‘_ci_vars‘, ‘_ci_path‘, ‘_ci_return‘) as $_ci_val)
 579   {
 580    $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
 581   }
 582
 583   $file_exists = FALSE;
 584
 585   //当Loader::_ci_load()方法是通过Loader::file()调用的时候,则会有$_ci_path的值,如果是
 586   //如果Loader::view()调用的话,则有$_ci_view的值。
 587   //如果$_ci_path不为空,则说明当前要加载普通文件。
 588   if ($_ci_path != ‘‘)
 589   {
 590    //普通文件。这里只是获得文件名,以便找不到报错时候只报文件名而已。
 591    $_ci_x = explode(‘/‘, $_ci_path);
 592    $_ci_file = end($_ci_x);
 593   }
 594   else
 595   {
 596    //视图文件。
 597    //下面两行操作也是为了让外部可以通过xxx.php或者直接xxx的方式进行传参而已。
 598    $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
 599    $_ci_file = ($_ci_ext == ‘‘) ? $_ci_view.‘.php‘ : $_ci_view;
 600
 601    foreach ($this->_ci_view_paths as $view_file => $cascade)
 602    {
 603     //从Loader::$_ci_view_paths中遍历视图文件,如果找到则退出。
 604     //(默认仅有APPPATH/view/下,当然也可以通过Loader::add_package()方法设置)
 605     if (file_exists($view_file.$_ci_file))
 606     {
 607      $_ci_path = $view_file.$_ci_file;
 608      $file_exists = TRUE;
 609      break;
 610     }
 611
 612     //如果没有找到,会根据这个$cascade判断允不允许继续往下一个路径寻找视图文件。
 613     if ( ! $cascade)
 614     {
 615      break;
 616     }
 617    }
 618   }
 619
 620   //如果找不到文件(普通或视图都一样),则报错。
 621   if ( ! $file_exists && ! file_exists($_ci_path))
 622   {
 623    show_error(‘Unable to load the requested file: ‘.$_ci_file);
 624   }
 625
 626   //下面这个也很关键,其实视图文件里面的代码都是在属于Loader组件的,什么意思?
 627   //你可以随便写一个视图文件,然后在里面写上var_dump($this);可以发现,这个$this,是指Loader。
 628   //为什么会这样子呢?再往下面十几行代码的地方就说明了这一点。
 629   //这里是把CI所有的属性都开放给Loader组件用,这样在视图文件里面就可以通过$this->xxx的方式调用控制器
 630   //所有的东西。
 631
 632   $_ci_CI =& get_instance();
 633   foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
 634   {
 635    if ( ! isset($this->$_ci_key))
 636    {
 637     $this->$_ci_key =& $_ci_CI->$_ci_key;
 638    }
 639   }
 640
 641   //在这里把在控制器里面通过$this->load->view("xxx",$data);中的$data解开,这就是为什么可以在视图文件
 642   //中可以用$data里面的变量的原因。其实还可以通过Loader::vars()方法,设置这些变量,它们会首先保存在
 643   //Loader::$_ci_cached_vars中
 644   if (is_array($_ci_vars))
 645   {
 646    $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
 647   }
 648   extract($this->_ci_cached_vars);
 649
 650   //我们在控制器中调用$this->load->view()方法,实质视图并没有马上输出来,而是先将它放到缓冲区。
 651   ob_start();
 652
 653   //就是这个地方,下面if中有一句eval(xxxx)以及else中有include;而里面的xxxx正是我们要加载的视图文件,
 654   //所以这就是为什么在视图文件里,var_dump($this),会告诉你当前这个$this是Loader组件,因为视图的代码都是相当于
 655   //嵌入这个地方。
 656   if ((bool) @ini_get(‘short_open_tag‘) === FALSE AND config_item(‘rewrite_short_tags‘) == TRUE)
 657   {
 658    echo eval(‘?>‘.preg_replace("/;*\s*\?>/", "; ?>", str_replace(‘<?=‘, ‘<?php echo ‘, file_get_contents($_ci_path))));//‘
 659   }
 660   else
 661   {
 662    include($_ci_path);
 663   }
 664
 665   //经过上面的代码,我们的视图文件的内容已经放到了缓冲区了。
 666
 667   log_message(‘debug‘, ‘File loaded: ‘.$_ci_path);
 668
 669   //一般情况下,$_ci_return都为FLASE,即不要求通过$this->load->view()返回输出内容,而是直接放到缓冲区静候处理;
 670   //当然你也可以先拿出数据,在控制器里面处理一下,再输出,例如在控制器中
 671   //$output=$this->load->view("x",$data,TRUE);,当为TRUE的时候,下面的代码就起作用了。
 672   if ($_ci_return === TRUE)
 673   {
 674    $buffer = ob_get_contents();
 675    @ob_end_clean();
 676    return $buffer;
 677   }
 678
 679   //下面这个很关键,因为有可能当前这个视图文件是被另一个视图文件通过$this->view()方法引入,即视图文件嵌入视图文件
 680   //从而导致多了一层缓冲。
 681   //为了保证缓冲内容最后交给Output处理时,缓冲级别只比Loader组件加载时多1(这个1就是最父层的视图文件引起的)
 682   //这里必须先flush掉当前层视图引起的这次缓冲,以保证Output正常工作。
 683   if (ob_get_level() > $this->_ci_ob_level + 1)
 684   {
 685    ob_end_flush();
 686   }
 687   else
 688   {
 689    //如果不是多1,则说明当前引入的视图文件就是直接在控制器里面引入的那个,而不是由某个视图文件再引入的。
 690
 691    //把缓冲区的内容交给Output组件并清空关闭缓冲区。
 692    $_ci_CI->output->append_output(ob_get_contents());
 693    @ob_end_clean();
 694   }
 695  }
 696
 697  // --------------------------------------------------------------------
 698
 699  /**
 700   * Load class
 701   */
 702  protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
 703  {
 704   //去掉后缀.php,是为了方便外部可以通过xxx.php也可以通过xxx.php来传入类名。同时去掉两端的/。
 705   $class = str_replace(‘.php‘, ‘‘, trim($class, ‘/‘));
 706
 707   //因为CI允许通过"dir1/dir2/classname"的格式来组织和加载类,所以还要判断类名中是否包括这些目录信息。
 708   $subdir = ‘‘;
 709   if (($last_slash = strrpos($class, ‘/‘)) !== FALSE)
 710   {
 711    //目录部分
 712    $subdir = substr($class, 0, $last_slash + 1);
 713
 714    //类名部分
 715    $class = substr($class, $last_slash + 1);
 716   }
 717
 718   //CI允许类文件以大写字母开头或者全小写,下面的遍历,就是在遍历这两种情况。
 719   foreach (array(ucfirst($class), strtolower($class)) as $class)
 720   {
 721    $subclass = APPPATH.‘libraries/‘.$subdir.config_item(‘subclass_prefix‘).$class.‘.php‘;
 722
 723    //是否有我们开发人员自己写的扩展当前类的扩展?如果有的话,把它加载进来。
 724    if (file_exists($subclass))
 725    {
 726     //先加载父类。
 727     $baseclass = BASEPATH.‘libraries/‘.ucfirst($class).‘.php‘;
 728
 729     if ( ! file_exists($baseclass))
 730     {
 731      log_message(‘error‘, "Unable to load the requested class: ".$class);
 732      show_error("Unable to load the requested class: ".$class);
 733     }
 734
 735     if (in_array($subclass, $this->_ci_loaded_files))
 736     {
 737
 738      if ( ! is_null($object_name))
 739      {
 740       $CI =& get_instance();
 741       //我们加载的类最终都是加载给超级控制器的,如果超级控制器已经有的话,那么我们没必要加载。
 742       //如果没有,则实例它并加载给控制器。,
 743       if ( ! isset($CI->$object_name))
 744       {
 745        return $this->_ci_init_class($class, config_item(‘subclass_prefix‘), $params, $object_name);
 746       }
 747      }
 748
 749      $is_duplicate = TRUE;
 750      log_message(‘debug‘, $class." class already loaded. Second attempt ignored.");
 751      return;
 752     }
 753
 754     //加载类。
 755     include_once($baseclass);
 756     include_once($subclass);
 757     //把已加载的类记录到Loader::_ci_loaded_files中。
 758     $this->_ci_loaded_files[] = $subclass;
 759
 760     //调用Loader::_ci_init_class()方法进而实例化。
 761     return $this->_ci_init_class($class, config_item(‘subclass_prefix‘), $params, $object_name);
 762    }
 763
 764    //如果是没有写扩展。方法和上面大致相同,最后都是通过调用Loader::_ci_init_class()方法进而实例化。
 765
 766    $is_duplicate = FALSE;
 767    foreach ($this->_ci_library_paths as $path)
 768    {
 769     $filepath = $path.‘libraries/‘.$subdir.$class.‘.php‘;
 770     if ( ! file_exists($filepath))
 771     {
 772      continue;
 773     }
 774
 775     if (in_array($filepath, $this->_ci_loaded_files))
 776     {
 777      if ( ! is_null($object_name))
 778      {
 779       $CI =& get_instance();
 780       if ( ! isset($CI->$object_name))
 781       {
 782        return $this->_ci_init_class($class, ‘‘, $params, $object_name);
 783       }
 784      }
 785
 786      $is_duplicate = TRUE;
 787      log_message(‘debug‘, $class." class already loaded. Second attempt ignored.");
 788      return;
 789     }
 790
 791     include_once($filepath);
 792     $this->_ci_loaded_files[] = $filepath;
 793     return $this->_ci_init_class($class, ‘‘, $params, $object_name);
 794    }
 795
 796   } // END FOREACH
 797
 798   //其实正常的话,上面如果找到此类就找到,没找到就没有了。不过CI在会这里做最后的尝试。会不会是放到了一个同名的
 799   //子目录下。
 800   if ($subdir == ‘‘)
 801   {
 802    $path = strtolower($class).‘/‘.$class;
 803    return $this->_ci_load_class($path, $params);
 804   }
 805
 806
 807   //没有找到就报错咯。
 808   if ($is_duplicate == FALSE)
 809   {
 810    log_message(‘error‘, "Unable to load the requested class: ".$class);
 811    show_error("Unable to load the requested class: ".$class);
 812   }
 813  }
 814
 815  // --------------------------------------------------------------------
 816
 817  //此方法是用于实例化已经把类文件include进来的类。
 818  protected function _ci_init_class($class, $prefix = ‘‘, $config = FALSE, $object_name = NULL)
 819  {
 820   // Is there an associated config file for this class?  Note: these should always be lowercase
 821   if ($config === NULL)
 822   {
 823    // Fetch the config paths containing any package paths
 824    $config_component = $this->_ci_get_component(‘config‘);
 825
 826    if (is_array($config_component->_config_paths))
 827    {
 828     // Break on the first found file, thus package files
 829     // are not overridden by default paths
 830     foreach ($config_component->_config_paths as $path)
 831     {
 832      // We test for both uppercase and lowercase, for servers that
 833      // are case-sensitive with regard to file names. Check for environment
 834      // first, global next
 835      if (defined(‘ENVIRONMENT‘) AND file_exists($path .‘config/‘.ENVIRONMENT.‘/‘.strtolower($class).‘.php‘))
 836      {
 837       include($path .‘config/‘.ENVIRONMENT.‘/‘.strtolower($class).‘.php‘);
 838       break;
 839      }
 840      elseif (defined(‘ENVIRONMENT‘) AND file_exists($path .‘config/‘.ENVIRONMENT.‘/‘.ucfirst(strtolower($class)).‘.php‘))
 841      {
 842       include($path .‘config/‘.ENVIRONMENT.‘/‘.ucfirst(strtolower($class)).‘.php‘);
 843       break;
 844      }
 845      elseif (file_exists($path .‘config/‘.strtolower($class).‘.php‘))
 846      {
 847       include($path .‘config/‘.strtolower($class).‘.php‘);
 848       break;
 849      }
 850      elseif (file_exists($path .‘config/‘.ucfirst(strtolower($class)).‘.php‘))
 851      {
 852       include($path .‘config/‘.ucfirst(strtolower($class)).‘.php‘);
 853       break;
 854      }
 855     }
 856    }
 857   }
 858
 859   if ($prefix == ‘‘)
 860   {
 861    if (class_exists(‘CI_‘.$class))
 862    {
 863     $name = ‘CI_‘.$class;
 864    }
 865    elseif (class_exists(config_item(‘subclass_prefix‘).$class))
 866    {
 867     $name = config_item(‘subclass_prefix‘).$class;
 868    }
 869    else
 870    {
 871     $name = $class;
 872    }
 873   }
 874   else
 875   {
 876    $name = $prefix.$class;
 877   }
 878
 879   // Is the class name valid?
 880   if ( ! class_exists($name))
 881   {
 882    log_message(‘error‘, "Non-existent class: ".$name);
 883    show_error("Non-existent class: ".$class);
 884   }
 885
 886   // Set the variable name we will assign the class to
 887   // Was a custom class name supplied?  If so we‘ll use it
 888   $class = strtolower($class);
 889
 890   if (is_null($object_name))
 891   {
 892    $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
 893   }
 894   else
 895   {
 896    $classvar = $object_name;
 897   }
 898
 899   // Save the class name and object name
 900   $this->_ci_classes[$class] = $classvar;
 901
 902   // Instantiate the class
 903   $CI =& get_instance();
 904   if ($config !== NULL)
 905   {
 906    $CI->$classvar = new $name($config);
 907   }
 908   else
 909   {
 910    $CI->$classvar = new $name;
 911   }
 912  }
 913
 914  // --------------------------------------------------------------------
 915
 916  /**
 917   * Autoloader
 918   *
 919   * The config/autoload.php file contains an array that permits sub-systems,
 920   * libraries, and helpers to be loaded automatically.
 921   *
 922   */
 923  private function _ci_autoloader()
 924  {
 925   if (defined(‘ENVIRONMENT‘) AND file_exists(APPPATH.‘config/‘.ENVIRONMENT.‘/autoload.php‘))
 926   {
 927    include(APPPATH.‘config/‘.ENVIRONMENT.‘/autoload.php‘);
 928   }
 929   else
 930   {
 931    include(APPPATH.‘config/autoload.php‘);
 932   }
 933
 934   if ( ! isset($autoload))
 935   {
 936    return FALSE;
 937   }
 938
 939   // Autoload packages
 940   if (isset($autoload[‘packages‘]))
 941   {
 942    foreach ($autoload[‘packages‘] as $package_path)
 943    {
 944     $this->add_package_path($package_path);
 945    }
 946   }
 947
 948   // Load any custom config file
 949   if (count($autoload[‘config‘]) > 0)
 950   {
 951    $CI =& get_instance();
 952    foreach ($autoload[‘config‘] as $key => $val)
 953    {
 954     $CI->config->load($val);
 955    }
 956   }
 957
 958   // Autoload helpers and languages
 959   foreach (array(‘helper‘, ‘language‘) as $type)
 960   {
 961    if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
 962    {
 963     $this->$type($autoload[$type]);
 964    }
 965   }
 966
 967   // A little tweak to remain backward compatible
 968   // The $autoload[‘core‘] item was deprecated
 969   if ( ! isset($autoload[‘libraries‘]) AND isset($autoload[‘core‘]))
 970   {
 971    $autoload[‘libraries‘] = $autoload[‘core‘];
 972   }
 973
 974   // Load libraries
 975   if (isset($autoload[‘libraries‘]) AND count($autoload[‘libraries‘]) > 0)
 976   {
 977    // Load the database driver.
 978    if (in_array(‘database‘, $autoload[‘libraries‘]))
 979    {
 980     $this->database();
 981     $autoload[‘libraries‘] = array_diff($autoload[‘libraries‘], array(‘database‘));
 982    }
 983
 984    // Load all other libraries
 985    foreach ($autoload[‘libraries‘] as $item)
 986    {
 987     $this->library($item);
 988    }
 989   }
 990
 991   // Autoload models
 992   if (isset($autoload[‘model‘]))
 993   {
 994    $this->model($autoload[‘model‘]);
 995   }
 996  }
 997
 998  // --------------------------------------------------------------------
 999
1000  /**
1001   * Object to Array
1002   *
1003   * Takes an object as input and converts the class variables to array key/vals
1004   *
1005   */
1006  protected function _ci_object_to_array($object)
1007  {
1008   return (is_object($object)) ? get_object_vars($object) : $object;
1009  }
1010
1011  // --------------------------------------------------------------------
1012
1013  /**
1014   * Get a reference to a specific library or model
1015   *
1016   */
1017  protected function &_ci_get_component($component)
1018  {
1019   $CI =& get_instance();
1020   return $CI->$component;
1021  }
1022
1023  // --------------------------------------------------------------------
1024
1025  /**
1026   * Prep filename
1027   *
1028   * This function preps the name of various items to make loading them more reliable.
1029   *
1030   */
1031  protected function _ci_prep_filename($filename, $extension)
1032  {
1033   if ( ! is_array($filename))
1034   {
1035    return array(strtolower(str_replace(‘.php‘, ‘‘, str_replace($extension, ‘‘, $filename)).$extension));
1036   }
1037   else
1038   {
1039    foreach ($filename as $key => $val)
1040    {
1041     $filename[$key] = strtolower(str_replace(‘.php‘, ‘‘, str_replace($extension, ‘‘, $val)).$extension);
1042    }
1043
1044    return $filename;
1045   }
1046  }
1047 }
时间: 2024-10-05 16:57:34

十三)CodeIgniter源码分析之Loader.php的相关文章

三)CodeIgniter源码分析之Common.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 // ------------------------------------------------------------------------ 4 5 /** 6 * Common Functions 7 */ 8 9 /** 10 * 为什么还要定义这些全局函数呢?比如说,下面有很多函数,如get_config().confi

十)CodeIgniter源码分析之Output.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 // ------------------------------------------------------------------------ 4 5 /** 6 * Output Class 7 * 8 * Output组件其实有很多有用的方法,不过一般情况下,你不会直接去用到它们. 9 * 这里主要以Output::_dis

二)CodeIgniter源码分析之CodeIgniter.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * 上面: * 这个BASEPATH,就是在入口文件(index.php)里面定义的那个BASEPATH- * 如果没有定义BASEPATH,那么直接退出,下面程序都不执行.其实除了入口文件index.php开头没有这句话之外,所有文件都会有这句话 * 也就是说,所有文件都不能单独运行,一定是index.php在运行过程中把这些文件通 *

十一)CodeIgniter源码分析之Controller.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 /** 4 * CodeIgniter Application Controller Class 5 */ 6 class CI_Controller { 7 8 private static $instance; 9 10 /** 11 * Constructor 12 */ 13 public function __construc

CodeIgniter源码分析之index.php

<?php /* *--------------------------------------------------------------- * APPLICATION ENVIRONMENT *--------------------------------------------------------------- * * * //配置项目运行的环境,该配置会影响错误报告的显示和配置文件的读取. */ define('ENVIRONMENT', 'development'); /*

七)CodeIgniter源码分析之Benchmark.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 // ------------------------------------------------------------------------ 3 4 /** 5 * CodeIgniter Benchmark Class 6 */ 7 class CI_Benchmark { 8 9 /** 10 * List of all be

九)CodeIgniter源码分析之Hook.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 // ------------------------------------------------------------------------ 3 4 /** 5 * CodeIgniter Hooks Class 6 */ 7 class CI_Hooks { 8 9 /** 10 * Determines wether hook

八)CodeIgniter源码分析之Config.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 // ------------------------------------------------------------------------ 4 5 /** 6 * CodeIgniter Config Class 7 */ 8 class CI_Config { 9 10 /** 11 * List of all loade

六)CodeIgniter源码分析之Exception.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 // ------------------------------------------------------------------------ 3 4 /** 5 * Exceptions Class 6 */ 7 class CI_Exceptions { 8 var $action; 9 var $severity; 10 va