PHP CI框架中如何实现类库的自动加载及别名逻辑处理

栏目: PHP · 发布时间: 5年前

内容简介:发现这个yredis没有load,怎么来的?翻翻手册,有自动加载配置在app/config/autoload.php中配置,部分内容如下

缘由

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这个类

结束


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

细节决定交互设计的成败

细节决定交互设计的成败

张亮 / 2009-3 / 49.00元

《细节决定交互设计的成败》是一本非常实用的有关软件界面的交互设计和可用性设计方面知识的书籍,通过采用一问一答的形式,你将会有针对性地学习到一些能够很快应用在自己软件开发工作中的细节知识和诀窍。例如,如何减轻用户的等待感,如何预防和减少用户的使用错误等。另外,你会发现阅读《细节决定交互设计的成败》时会非常轻松和愉悦;这是由于《细节决定交互设计的成败》写作上的两个特点:第一,采用较多日常生活中的例子来......一起来看看 《细节决定交互设计的成败》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具