缘由

app/controllers/Index.php中有如下代码

public function disable(){        $this->yredis->set('name','tb');        var_dump($this->yredis->get('name'));        $this->load->view('welcome_message');    }

发现这个yredis没有load,怎么来的?翻翻手册,有自动加载配置

在app/config/autoload.php中配置,部分内容如下
| -------------------------------------------------------------------|  Auto-load Libraries| -------------------------------------------------------------------| These are the classes located in system/libraries/ or your| application/libraries/ directory, with the addition of the| 'database' library, which is somewhat of a special case.|| Prototype:||    $autoload['libraries'] = array('database', 'email', 'session');|| You can also supply an alternative library name to be assigned| in the controller:||    $autoload['libraries'] = array('user_agent' => 'ua');*/$autoload['libraries'] = array('Yredis','validation'=>'sn');

追踪一下

那么他这个自动加载是在代码内如何实现的呢?肯定从头$CI大对象来看。
system/core/Controller.php中,有如下两句

$this->load =& load_class('Loader', 'core');$this->load->initialize();

通过common.php中定义的load_class方法,加载了loader类。不得不说ci的核心基本都在这里了。(这个load还是个未定义的属性么...?)
我们去看system/core/Loader.php中的initialize方法,不过load的时候肯定是先加载__construct方法

public function __construct()    {        // (ob机制,不要乱配~)         $this->_ci_ob_level = ob_get_level();              $this->_ci_classes =& is_loaded();        log_message('info', 'Loader Class Initialized');    }

再回来看当前文件的initialize方法:

    public function initialize()    {        $this->_ci_autoloader();    }

再去看_ci_autoloader...

    protected function _ci_autoloader()    {        if (file_exists(APPPATH.'config/autoload.php'))        {            include(APPPATH.'config/autoload.php');        }        if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))        {            include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');        }        if ( ! isset($autoload))        {            return;        }        // Autoload packages        if (isset($autoload['packages']))        {            foreach ($autoload['packages'] as $package_path)            {                $this->add_package_path($package_path);            }        }        // Load any custom config file        if (count($autoload['config']) > 0)        {            foreach ($autoload['config'] as $val)            {                $this->config($val);            }        }        // Autoload helpers and languages        foreach (array('helper', 'language') as $type)        {            if (isset($autoload[$type]) && count($autoload[$type]) > 0)            {                $this->$type($autoload[$type]);            }        }        // Autoload drivers        if (isset($autoload['drivers']))        {            $this->driver($autoload['drivers']);        }        // Load libraries        if (isset($autoload['libraries']) && 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 all other libraries            $this->library($autoload['libraries']);        }        // Autoload models        if (isset($autoload['model']))        {            $this->model($autoload['model']);        }    }

这里总算有点眉目,定位到了加载开头配置的autoload.php的位置,
include(APPPATH.'config/autoload.php');
不难得出,是分别处理配置段的packages,config,helper,language,dirvers,libraries(包含database在内),我们只是看library,于是定位到

$this->library($autoload['libraries']);

这里,看library方法

public function library($library, $params = NULL, $object_name = NULL)    {        if (empty($library))        {            return $this;        }        elseif (is_array($library))        {            foreach ($library as $key => $value)            {                if (is_int($key))                {                    $this->library($value, $params);                }                else                {                    $this->library($key, $params, $value);                }            }            return $this;        }        if ($params !== NULL && ! is_array($params))        {            $params = NULL;        }        $this->_ci_load_library($library, $params, $object_name);        return $this;    }

froeach中的if else 就是判断是否使用了别名,就像开头在autoload.php 中配置
$autoload['libraries'] = array('Yredis','validation'=>'sn');的第二个参数那样,这样我们在项目中可以显示使用$this->sn->xx()执行了。当然到这还没完,虽然最后返回的是$this,但是之前执行了关键的一步

$this->_ci_load_library($library, $params, $object_name);

我们继续看_ci_load_library方法

 protected function _ci_load_library($class, $params = NULL, $object_name = NULL)    {        // Get the class name, and while we're at it trim any slashes.        // The directory path can be included as part of the class name,        // but we don't want a leading slash        $class = str_replace('.php', '', trim($class, '/'));        // Was the path included with the class name?        // We look for a slash to determine this        if (($last_slash = strrpos($class, '/')) !== FALSE)        {            // Extract the path            $subdir = substr($class, 0, ++$last_slash);            // Get the filename from the path            $class = substr($class, $last_slash);        }        else        {            $subdir = '';        }        $class = ucfirst($class);        // Is this a stock library? There are a few special conditions if so ...        if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))        {            return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);        }        // Safety: Was the class already loaded by a previous call?        if (class_exists($class, FALSE))        {            $property = $object_name;            if (empty($property))            {                $property = strtolower($class);                isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];            }            $CI =& get_instance();            if (isset($CI->$property))            {                log_message('debug', $class.' class already loaded. Second attempt ignored.');                return;            }            return $this->_ci_init_library($class, '', $params, $object_name);        }        // Let's search for the requested library file and load it.        foreach ($this->_ci_library_paths as $path)        {            // BASEPATH has already been checked for            if ($path === BASEPATH)            {                continue;            }            $filepath = $path.'libraries/'.$subdir.$class.'.php';            // Does the file exist? No? Bummer...            if ( ! file_exists($filepath))            {                continue;            }            include_once($filepath);            return $this->_ci_init_library($class, '', $params, $object_name);        }        // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?        if ($subdir === '')        {            return $this->_ci_load_library($class.'/'.$class, $params, $object_name);        }        // If we got this far we were unable to find the requested class.        log_message('error', 'Unable to load the requested class: '.$class);        show_error('Unable to load the requested class: '.$class);    }

大家可以看多个return的前导条件,我们最后追到_ci_init_library方法。如下:

protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)    {        // Is there an associated config file for this class? Note: these should always be lowercase        if ($config === NULL)        {            // Fetch the config paths containing any package paths            $config_component = $this->_ci_get_component('config');            if (is_array($config_component->_config_paths))            {                $found = FALSE;                foreach ($config_component->_config_paths as $path)                {                    // We test for both uppercase and lowercase, for servers that                    // are case-sensitive with regard to file names. Load global first,                    // override with environment next                    if (file_exists($path.'config/'.strtolower($class).'.php'))                    {                        include($path.'config/'.strtolower($class).'.php');                        $found = TRUE;                    }                    elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))                    {                        include($path.'config/'.ucfirst(strtolower($class)).'.php');                        $found = TRUE;                    }                    if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))                    {                        include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');                        $found = TRUE;                    }                    elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))                    {                        include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');                        $found = TRUE;                    }                    // Break on the first found configuration, thus package                    // files are not overridden by default paths                    if ($found === TRUE)                    {                        break;                    }                }            }        }        $class_name = $prefix.$class;        // Is the class name valid?        if ( ! class_exists($class_name, FALSE))        {            log_message('error', 'Non-existent class: '.$class_name);            show_error('Non-existent class: '.$class_name);        }        // Set the variable name we will assign the class to        // Was a custom class name supplied? If so we'll use it        if (empty($object_name))        {            $object_name = strtolower($class);            if (isset($this->_ci_varmap[$object_name]))            {                $object_name = $this->_ci_varmap[$object_name];            }        }        // Don't overwrite existing properties        $CI =& get_instance();        if (isset($CI->$object_name))        {            if ($CI->$object_name instanceof $class_name)            {                log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");                return;            }            show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");        }        // Save the class name and object name        $this->_ci_classes[$object_name] = $class;        var_dump($class);        // Instantiate the class        $CI->$object_name = isset($config)            ? new $class_name($config)            : new $class_name();    }

注意多条件下的大小写处理(strtolowerucfirst),等想出坑(如果有)的时候别忘记这里就好。走到最后就是这关键货

$CI->$object_name = isset($config)? new $class_name($config): new $class_name();

到这里就很清楚了,最后参考CI大对象,输出部分如下

    //...    ["load"]=>  &object(Api_Loader)#13 (12) {    ["_ci_services_paths":protected]=>    array(1) {      [0]=>      string(45) "/usr/share/nginx/xin_mount_dev/trunk/app/"    }    ["_ci_services":protected]=>    array(0) {    }    ["_ci_ob_level":protected]=>    int(1)    ["_ci_view_paths":protected]=>    array(1) {      ["/usr/share/nginx/xin_mount_dev/trunk/app/views/"]=>      bool(true)    }    ["_ci_library_paths":protected]=>    array(2) {      [0]=>      string(45) "/usr/share/nginx/xin_mount_dev/trunk/app/"      [1]=>      string(24) "/usr/share/nginx/ci_3.0/"    }    ["_ci_model_paths":protected]=>    array(1) {      [0]=>      string(45) "/usr/share/nginx/xin_mount_dev/trunk/app/"    }    ["_ci_helper_paths":protected]=>    array(2) {      [0]=>      string(45) "/usr/share/nginx/xin_mount_dev/trunk/app/"      [1]=>      string(24) "/usr/share/nginx/ci_3.0/"    }    ["_ci_cached_vars":protected]=>    array(0) {    }    ["_ci_classes":protected]=>    &array(15) {      ["benchmark"]=>      string(9) "Benchmark"      ["hooks"]=>      string(5) "Hooks"      ["config"]=>      string(6) "Config"      ["log"]=>      string(3) "Log"      ["utf8"]=>      string(4) "Utf8"      ["uri"]=>      string(3) "URI"      ["router"]=>      string(6) "Router"      ["output"]=>      string(6) "Output"      ["security"]=>      string(8) "Security"      ["input"]=>      string(5) "Input"      ["lang"]=>      string(4) "Lang"      ["loader"]=>      string(6) "Loader"      ["yredis"]=>      string(6) "Yredis"      ["sn"]=>      string(10) "Validation"    }    ["_ci_models":protected]=>    array(0) {    }    ["_ci_helpers":protected]=>    array(0) {    }    ["_ci_varmap":protected]=>    array(2) {      ["unit_test"]=>      string(4) "unit"      ["user_agent"]=>      string(5) "agent"    }  }  ["yredis"]=>  object(Yredis)#14 (2) {    ["_redis":"Yredis":private]=>    object(Redis)#15 (0) {    }    ["_redis_conf":"Yredis":private]=>    array(2) {      ["hostname"]=>      string(9) "127.0.0.1"      ["port"]=>      string(4) "6379"    }  }  ["sn"]=>  object(Validation)#16 (6)  // ...

关注yredis和sn就更清楚啦,注意sn是别名,而实际指向的是Validation这个类

结束