用户
 找回密码
 入住 CI 中国社区
搜索
查看: 13508|回复: 9
收起左侧

ci源码分析--libraries/loader

[复制链接]
发表于 2009-3-11 13:26:07 | 显示全部楼层 |阅读模式
PHP复制代码
 
<?php  if ( ! defined('BASEPATH')) exit('不允许直接访问');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 4.3.2 or newer
 *
 * @package  CodeIgniter
 * @author  ExpressionEngine Dev Team
 * @copyright Copyright (c) 2008, EllisLab, Inc.
 * @license  http://codeigniter.com/user_guide/license.html
 * @link  http://codeigniter.com
 * @since  Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------
/**
 * Loader Class
 *
 * Loads views and files
 *
 * @package  CodeIgniter
 * @subpackage Libraries
 * @author  ExpressionEngine Dev Team
 * @修改        icebolt(29142986)
 * @category Loader
 * @link  http://codeigniter.com/user_guide/libraries/loader.html
 */

class CI_Loader {
 var $_ci_ob_level;
 var $_ci_view_path  = '';
 var $_ci_is_php5  = FALSE;
 var $_ci_is_instance  = FALSE;
 var $_ci_cached_vars = array();
 var $_ci_classes  = array();
 var $_ci_loaded_files = array();
 var $_ci_models   = array();
 var $_ci_helpers  = array();
 var $_ci_plugins  = array();
 var $_ci_varmap   = array('unit_test' => 'unit', 'user_agent' => 'agent');
 
 function CI_Loader()
 {
  $this->_ci_is_php5 = (floor(phpversion()) >= 5) ? TRUE : FALSE;
  $this->_ci_view_path = APPPATH.'views/';
  $this->_ci_ob_level  = ob_get_level();
   
  log_message('debug', "Loader类初始化成功");
 }
 
 //这个函数是用来加载核心类。
 function library($library = '', $params = NULL, $object_name = NULL)
 {
  //第一个参数不能为空
  if ($library == '')
  {
   return FALSE;
  }
  //第二个参数必须是一个数组,否则为空
  if ( ! is_null($params) AND ! is_array($params))
  {
   $params = NULL;
  }
  if (is_array($library))
  {
   foreach ($library as $class)
   {
    $this->_ci_load_class($class, $params, $object_name);
   }
  }
  else
  {
   $this->_ci_load_class($library, $params, $object_name);
  }
 
  $this->_ci_assign_to_models();
 }
 //载入模块
 function model($model, $name = '', $db_conn = FALSE)
 {  
  if (is_array($model))
  {
   foreach($model as $babe)
   {
    $this->model($babe);
   }
   return;
  }
  if ($model == '')
  {
   return;
  }
 
  if (strpos($model, '/') === FALSE)
  {
   $path = '';
  }
  else
  {
   $x = explode('/', $model);
   $model = end($x);  
   unset($x[count($x)-1]);
   $path = implode('/', $x).'/';
  }
 
  if ($name == '')
  {
   $name = $model;
  }
 
  if (in_array($name, $this->_ci_models, TRUE))
  {
   return;
  }
 
  $CI =& get_instance();
  if (isset($CI->$name))
  {
   show_error('模块名已经被占用: '.$name);
  }
 
  $model = strtolower($model);
 
  if ( ! file_exists(APPPATH.'models/'.$path.$model.EXT))
  {
   show_error('找不到模块文件: '.$model);
  }
   
  if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
  {
   if ($db_conn === TRUE)
    $db_conn = '';
 
   $CI->load->database($db_conn, FALSE, TRUE);
  }
 
  if ( ! class_exists('Model'))
  {
   load_class('Model', FALSE);
  }
  require_once(APPPATH.'models/'.$path.$model.EXT);
  $model = ucfirst($model);
   
  $CI->$name = new $model();
  $CI->$name->_assign_libraries();
 
  $this->_ci_models[] = $name;
 }
 
 //载入数据库连接
 function database($params = '', $return = FALSE, $active_record = FALSE)
 {
  $CI =& get_instance();
  if (class_exists('CI_DB') AND $return == FALSE AND $active_record == FALSE AND isset($CI->db) AND is_object($CI->db))
  {
   return FALSE;
  }
 
  require_once(BASEPATH.'database/DB'.EXT);
  if ($return === TRUE)
  {
   return DB($params, $active_record);
  }
  $CI->db = '';
  $CI->db =& DB($params, $active_record);
  $this->_ci_assign_to_models();
 }
 
 //载入数据库工具类
 function dbutil()
 {
  if ( ! class_exists('CI_DB'))
  {
   $this->database();
  }
 
  $CI =& get_instance();
  $CI->load->dbforge();
 
  require_once(BASEPATH.'database/DB_utility'.EXT);
  require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility'.EXT);
  $class = 'CI_DB_'.$CI->db->dbdriver.'_utility';
  $CI->dbutil =& new $class();
  $CI->load->_ci_assign_to_models();
 }
 
 //载入数据库工厂类  
 function dbforge()
 {
  if ( ! class_exists('CI_DB'))
  {
   $this->database();
  }
 
  $CI =& get_instance();
 
  require_once(BASEPATH.'database/DB_forge'.EXT);
  require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT);
  $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';
  $CI->dbforge = new $class();
 
  $CI->load->_ci_assign_to_models();
 }
 
 //载入视图
 function view($view, $vars = array(), $return = FALSE)
 {
  return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
 }
 
 //载入一个文件
 function file($path, $return = FALSE)
 {
  return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
 }
 
 //让视图可以直接调用控制器的变量
 function vars($vars = array(), $val = '')
 {
  if ($val != '' AND is_string($vars))
  {
   $vars = array($vars => $val);
  }
 
  $vars = $this->_ci_object_to_array($vars);
 
  if (is_array($vars) AND count($vars) > 0)
  {
   foreach ($vars as $key => $val)
   {
    $this->_ci_cached_vars[$key] = $val;
   }
  }
 }
 
 //载入助手类
 function helper($helpers = array())
 {
  if ( ! is_array($helpers))
  {
   $helpers = array($helpers);
  }
 
  foreach ($helpers as $helper)
  {  
   $helper = strtolower(str_replace(EXT, '', str_replace('_helper', '', $helper)).'_helper');
   if (isset($this->_ci_helpers[$helper]))
   {
    continue;
   }
   
   $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.EXT;
   if (file_exists($ext_helper))
   {
    $base_helper = BASEPATH.'helpers/'.$helper.EXT;
   
    if ( ! file_exists($base_helper))
    {
     show_error('不能载入需要的文件: helpers/'.$helper.EXT);
    }
   
    include_once($ext_helper);
    include_once($base_helper);
   }
   elseif (file_exists(APPPATH.'helpers/'.$helper.EXT))
   {
    include_once(APPPATH.'helpers/'.$helper.EXT);
   }
   else
   {  
    if (file_exists(BASEPATH.'helpers/'.$helper.EXT))
    {
     include_once(BASEPATH.'helpers/'.$helper.EXT);
    }
    else
    {
     show_error('不能载入需要的文件: helpers/'.$helper.EXT);
    }
   }
   $this->_ci_helpers[$helper] = TRUE;
   log_message('debug', '载入助手类: '.$helper);
  }  
 }
 
 //载入助手类的别名
 function helpers($helpers = array())
 {
  $this->helper($helpers);
 }
 
 //载入插件
 function plugin($plugins = array())
 {
  if ( ! is_array($plugins))
  {
   $plugins = array($plugins);
  }
 
  foreach ($plugins as $plugin)
  {
   $plugin = strtolower(str_replace(EXT, '', str_replace('_pi', '', $plugin)).'_pi');  
   if (isset($this->_ci_plugins[$plugin]))
   {
    continue;
   }
   if (file_exists(APPPATH.'plugins/'.$plugin.EXT))
   {
    include_once(APPPATH.'plugins/'.$plugin.EXT);
   }
   else
   {
    if (file_exists(BASEPATH.'plugins/'.$plugin.EXT))
    {
     include_once(BASEPATH.'plugins/'.$plugin.EXT);
    }
    else
    {
     show_error('不能载入需要的文件: plugins/'.$plugin.EXT);
    }
   }
   
   $this->_ci_plugins[$plugin] = TRUE;
   log_message('debug', '载入插件: '.$plugin);
  }  
 }
 //载入插件的别名
 function plugins($plugins = array())
 {
  $this->plugin($plugins);
 }
 
 //载入语言文件
 function language($file = array(), $lang = '')
 {
  $CI =& get_instance();
  if ( ! is_array($file))
  {
   $file = array($file);
  }
  foreach ($file as $langfile)
  {
   $CI->lang->load($langfile, $lang);
  }
 }
 //载入脚手架的语言文件,没多大用,可以替换掉
 function scaffold_language($file = '', $lang = '', $return = FALSE)
 {
  $CI =& get_instance();
  return $CI->lang->load($file, $lang, $return);
 }
 
 //载入配置文件
 function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
 {  
  $CI =& get_instance();
  $CI->config->load($file, $use_sections, $fail_gracefully);
 }
 //开启脚手架功能
 function scaffolding($table = '')
 {  
  if ($table === FALSE)
  {
   show_error('必须输入一个你要控制的数据库表');
  }
 
  $CI =& get_instance();
  $CI->_ci_scaffolding = TRUE;
  $CI->_ci_scaff_table = $table;
 }
 //载入器,用于载入视图和文件等
 function _ci_load($_ci_data)
 {
  // Set the default data variables
  foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
  {
   $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
  }
  // Set the path to the requested file
  if ($_ci_path == '')
  {
   $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
   $_ci_file = ($_ci_ext == '') ? $_ci_view.EXT : $_ci_view;
   $_ci_path = $this->_ci_view_path.$_ci_file;
  }
  else
  {
   $_ci_x = explode('/', $_ci_path);
   $_ci_file = end($_ci_x);
  }
 
  if ( ! file_exists($_ci_path))
  {
   show_error('Unable to load the requested file: '.$_ci_file);
  }
 
  // This allows anything loaded using $this->load (views, files, etc.)
  // to become accessible from within the Controller and Model functions.
  // Only needed when running PHP 5
 
  if ($this->_ci_is_instance())
  {
   $_ci_CI =& get_instance();
   foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
   {
    if ( ! isset($this->$_ci_key))
    {
     $this->$_ci_key =& $_ci_CI->$_ci_key;
    }
   }
  }
  /*
   * Extract and cache variables
   *
   * You can either set variables using the dedicated $this->load_vars()
   * function or via the second parameter of this function. We'll merge
   * the two types and cache them so that views that are embedded within
   * other views can have access to these variables.
   */

  if (is_array($_ci_vars))
  {
   $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
  }
  extract($this->_ci_cached_vars);
   
  /*
   * Buffer the output
   *
   * We buffer the output for two reasons:
   * 1. Speed. You get a significant speed boost.
   * 2. So that the final rendered template can be
   * post-processed by the output class.  Why do we
   * need post processing?  For one thing, in order to
   * show the elapsed page load time.  Unless we
   * can intercept the content right before it's sent to
   * the browser and then stop the timer it won't be accurate.
   */

  ob_start();
   
  // If the PHP installation does not support short tags we'll
  // do a little string replacement, changing the short tags
  // to standard PHP echo statements.
 
  if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
  {
   echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path)))); //'
  }
  else
  {
   include($_ci_path); // include() vs include_once() allows for multiple views with the same name
  }
 
  log_message('debug', '载入文件: '.$_ci_path);
 
  // Return the file data if requested
  if ($_ci_return === TRUE)
  {  
   $buffer = ob_get_contents();
   @ob_end_clean();
   return $buffer;
  }
  if (ob_get_level() > $this->_ci_ob_level + 1)
  {
   ob_end_flush();
  }
  else
  {
   global $OUT;
   $OUT->append_output(ob_get_contents());
   @ob_end_clean();
  }
 }
 //私有函数,载入类
 function _ci_load_class($class, $params = NULL, $object_name = NULL)
 {
  //去除扩展名和前后的/
  $class = str_replace(EXT, '', trim($class, '/'));
 
  $subdir = '';
  if (strpos($class, '/') !== FALSE)
  {
   //用‘/’分解字符串
   $x = explode('/', $class);
   
   //获取最后元素一个为类名
   $class = end($x);
   
   //删除这个元素
   unset($x[count($x)-1]);
   
   //获得类名的目录
   $subdir = implode($x, '/').'/';
  }
  //最小化全部字符,大写第一个字母,两种方法进行载入
  foreach (array(ucfirst($class), strtolower($class)) as $class)
  {
   //继承类文件路径
   $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.EXT;
   //继承类文件是否存在  
   if (file_exists($subclass))
   {
    //主类文件是否存在
    $baseclass = BASEPATH.'libraries/'.ucfirst($class).EXT;
   
    if ( ! file_exists($baseclass))
    {
     log_message('error', "不能载入类: ".$class);
     show_error("不能载入类: ".$class);
    }
    //继承类是否已经载入
    if (in_array($subclass, $this->_ci_loaded_files))
    {
     //是否用另外一个名字声明类
     if ( ! is_null($object_name))
     {
      $CI =& get_instance();
      if ( ! isset($CI->$object_name))
      {
       return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);  
      }
     }
     
     $is_duplicate = TRUE;
     log_message('debug', $class."已经载入.");
     return;
    }
 
    include_once($baseclass);
    include_once($subclass);
    $this->_ci_loaded_files[] = $subclass;
 
    return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);  
   }
 
   $is_duplicate = FALSE;
   
   //分别通过 APPPATH BASEPATH两个目录尝试载入类文件
   for ($i = 1; $i < 3; $i++)
   {
    //分别通过 APPPATH BASEPATH两个目录尝试载入类文件
    $path = ($i % 2) ? APPPATH : BASEPATH;
    $filepath = $path.'libraries/'.$subdir.$class.EXT;
   
    //文件是否存在
    if ( ! file_exists($filepath))
    {
     continue;
    }
   
    //类是否已经载入
    if (in_array($filepath, $this->_ci_loaded_files))
    {
     //是否用另外一个名字声明类
     if ( ! is_null($object_name))
     {
      $CI =& get_instance();
      if ( ! isset($CI->$object_name))
      {
       return $this->_ci_init_class($class, '', $params, $object_name);
      }
     }
   
     $is_duplicate = TRUE;
     log_message('debug', $class."已经载入.");
     return;
    }
   
    include_once($filepath);
    $this->_ci_loaded_files[] = $filepath;
    return $this->_ci_init_class($class, '', $params, $object_name);
   }
  }
  // One last attempt.  Maybe the library is in a subdirectory, but it wasn't specified?
  //不知道这块有什么作用,没多大用,估计是考虑使用者没有输入类名,只输入了路径名,有时间测试一下看看,不过不会有这么弱智的使用者吧
  if ($subdir == '')
  {
   $path = strtolower($class).'/'.$class;
   return $this->_ci_load_class($path, $params);
  }
 
  //不能载入重复的类
  if ($is_duplicate == FALSE)
  {
   log_message('error', "不能载入重复的类: ".$class);
   show_error("不能载入重复的类: ".$class);
  }
 }
 
 //私有函数,初始化类
 function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
 {
  //配置参数为空,尝试载入config里面有没有配置文件
  if ($config === NULL)
  {
   if (file_exists(APPPATH.'config/'.strtolower($class).EXT))
   {
    include_once(APPPATH.'config/'.strtolower($class).EXT);
   }  
   else
   {
    if (file_exists(APPPATH.'config/'.ucfirst(strtolower($class)).EXT))
    {
     include_once(APPPATH.'config/'.ucfirst(strtolower($class)).EXT);
    }  
   }
  }
  //类前缀为空
  if ($prefix == '')
  {  
   if (class_exists('CI_'.$class))
   {
    $name = 'CI_'.$class;
   }
   elseif (class_exists(config_item('subclass_prefix').$class))
   {
    $name = config_item('subclass_prefix').$class;
   }
   else
   {
    $name = $class;
   }
  }
  else
  {
   $name = $prefix.$class;
  }
 
  //是否已经载入过这个类
  if ( ! class_exists($name))
  {
   log_message('error', "指定类不存在: ".$name);
   show_error("指定类不存在: ".$class);
  }
 
  $class = strtolower($class);
 
  if (is_null($object_name))
  {
   $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
  }
  else
  {
   $classvar = $object_name;
  }
  //记住载入的类名
  $this->_ci_classes[$class] = $classvar;
  //实例化类
  $CI =& get_instance();
  if ($config !== NULL)
  {
   $CI->$classvar = new $name($config);
  }
  else
  {  
   $CI->$classvar = new $name;
  }
 }  
 
 //自动载入类、助手类,插架,语言等
 function _ci_autoloader()
 {
  //载入自动载入配置文件
  include_once(APPPATH.'config/autoload'.EXT);
  //如果配置文件为空,直接返回
  if ( ! isset($autoload))
  {
   return FALSE;
  }
 
  // 载入配置项
  if (count($autoload['config']) > 0)
  {  
   $CI =& get_instance();
   foreach ($autoload['config'] as $key => $val)
   {
    $CI->config->load($val);
   }
  }  
  //通过循环载入helper、plugin、language
  foreach (array('helper', 'plugin', 'language') as $type)
  {  
   if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
   {
    $this->$type($autoload[$type]);
   }  
  }
  // A little tweak to remain backward compatible
  // The $autoload['core'] item was deprecated
  if ( ! isset($autoload['libraries']))
  {
   $autoload['libraries'] = $autoload['core'];
  }
 
  // Load libraries
  if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
  {
   // Load the database driver.
   if (in_array('database', $autoload['libraries']))
   {
    $this->database();
    $autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
   }
   // Load scaffolding
   if (in_array('scaffolding', $autoload['libraries']))
   {
    $this->scaffolding();
    $autoload['libraries'] = array_diff($autoload['libraries'], array('scaffolding'));
   }
 
   // Load all other libraries
   foreach ($autoload['libraries'] as $item)
   {
    $this->library($item);
   }
  }  
  // Autoload models
  if (isset($autoload['model']))
  {
   $this->model($autoload['model']);
  }
 }
 
 //
 function _ci_assign_to_models()
 {
  if (count($this->_ci_models) == 0)
  {
   return;
  }
 
  if ($this->_ci_is_instance())
  {
   $CI =& get_instance();
   foreach ($this->_ci_models as $model)
   {  
    $CI->$model->_assign_libraries();
   }
  }
  else
  {  
   foreach ($this->_ci_models as $model)
   {  
    $this->$model->_assign_libraries();
   }
  }
 }  
 //对象转化成数组
 function _ci_object_to_array($object)
 {
  return (is_object($object)) ? get_object_vars($object) : $object;
 }
 //
 function _ci_is_instance()
 {
  if ($this->_ci_is_php5 == TRUE)
  {
   return TRUE;
  }
 
  global $CI;
  return (is_object($CI)) ? TRUE : FALSE;
 }
}
 
复制代码
 楼主| 发表于 2009-3-11 13:27:58 | 显示全部楼层
ci的真正的核心的核心,希望大家能对这个文件仔细掌握,因为ci的大部分的mvc是在这里实现的
 楼主| 发表于 2009-3-11 13:29:57 | 显示全部楼层
php函数提示
extract
这个函数在这里有用到,有的可能不太熟悉,这个是把数组的分解
比如
PHP复制代码
 
$test_arr=array("aaa"=>"bbb");
extract($test_arr)
 
复制代码

就相当于
PHP复制代码
 
$aaa="bbb";
 
复制代码
 楼主| 发表于 2009-3-11 13:30:30 | 显示全部楼层
有什么问题,希望大家即时交流
发表于 2009-3-11 22:55:18 | 显示全部楼层
顶一个。
 楼主| 发表于 2009-3-12 11:51:11 | 显示全部楼层
下载了ci的最新版本,重新修改了一下
发表于 2009-3-12 13:54:15 | 显示全部楼层
我帮楼主修正了一下语法高亮,呵呵
发表于 2009-3-12 17:31:56 | 显示全部楼层
楼主辛苦了,不过只是些注释的简单翻译,希望楼主能写点分析Why的文章。支持
发表于 2009-5-20 05:06:09 | 显示全部楼层
没感觉分析了多少
发表于 2010-6-10 11:21:27 | 显示全部楼层
楼主辛苦了,不过只是些注释的简单翻译,希望楼主能写点分析Why的文章。支持 ...
recolee 发表于 2009-3-12 17:31


说的好!
why?how?都没怎么讲

本版积分规则