CI框架源码解析十六之加载器类文件Loader.php

        加载器,顾名思义,是用于加载元素的,加载的元素可以是库(类),视图文件 ,驱动器 ,辅助函数 ,模型或其他你自己的文件。本篇并不是对某一组件的详细源码分析,而只是简单的跟踪了下CI框架的autoload的基本流程。因此,可以看做是Loader组件的分析前提。

        CI框架中,允许你配置autoload数组,这样,在你的应用程序初始化时,会自动加载相应的类库,例如,在application/config/autoload.php中,autoload的配置如下:

    $autoload['libraries'] = array("smarty", "redis");

        则CI框架初始化时,会自动加载libraries下面的smarty.php和redis.php,并且在你的应用程序控制器中,可以通过$this->smarty->xxx  和$this->redis->yyy的方式调用你的类库。

CI允许autoload中配置的自动加载的类别有:
        ① Packages ---包
        ② Libraries  --类库
        ③ Helper files   ---用户自定义的辅助文件
        ④ Custom config files    ---用户自定义配置文件
        ⑤ Language files    ---语言包
        ⑥ Models    ---模型类

        如果想要知道CI框架的自动加载是如何是想的,我们可以以Libraries的自动加载为例,在追踪CI框架的autoload之路。由于Loader是CI框架中组件加载的管理器,而Loader是在CI_Controller中被加载的,因此我们可以从Controller加载Loader组件开始追踪。在CI_Controller中追踪到这样一句话:

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

        于是我们猜想,在Loader的initialize的过程中,对autoload做了相应的处理。Initialize的前面四个语句,用于对本身的属性、参数等初始化,不是我们需要关心的内容,真正执行autoload的应该是$this->_ci_autoloader(),沿着该线索,我们进入_ci_autoload的内部。

        _ci_autoloader的方法,首先引入autoload的配置数组;然后由于我们这次只追踪libraries的autoload机制,因此我们略过对packages和config等的autoload处理机制,而直接寻找对libraries的处理,可以看出,对所有的autoload的libraries,实际上是执行了library方法,再次进入library方法查看;上述对该方法的调用中,传递的是autoload中配置的类库名(我们的例子是redis和smarty),通过library方法的具体实现,可以看出,如果$library是数组,则会循环调用library方法。实际上最终会调用$this->_ci_load_class。再次进入_ci_load_class查看。最后到这里,我们总算了解了CI的autoload的基本流程(漫漫长征),作为对Loader组件的初步追踪,我们省略了中的许多实现细节,这些我们将在对Loader组件的分析过程中慢慢添加上。

总结一下autoload的基本流程:
        ① Application/config/autoload.php中配置需要autoload的类库;
        ② Controller实例化的时候,会加载Loader组件,并调用该组件的initialize方法,对需要的资源初始化;
        ③ 经过更多的错误检查和安全性检查的步骤,加载需要的类库、配置等。

        最后说一句,并不是所有的类库都需要通过CI的autoload加载,因为该类库在框架初始化的时候就被加载,而不管你是不是需要使用该类库,这样实际上会有一定的性能损失。如果你的类库并不是所有应用都需要的,那么,更好的方法是需要时再加载。

        关于CI框架的加载器类以及如何进行自动加载的方法就讲解这么多了,最后贴一个整个加载器类Loader.php文件的源码(注释版):

    <?php
    
    /**
     * =======================================
     * Created by Pocket Knife Technology.
     * User: ZhiHua_W
     * Date: 2016/10/28 0031
     * Time: 下午 2:31
     * Project: CodeIgniter框架—源码分析
     * Power: Analysis for Loader.php
     * =======================================
     */
    
    defined('BASEPATH') OR exit('No direct script access allowed');
    
    /**
     * 加载器类
     * 加载器,顾名思义,是用于加载元素的,
     * 加载的元素可以是库(类),视图文件 ,驱动器 ,辅助函数 ,模型
     * 或其他你自己的文件。
     * Loader组件在CI里面也是一个很重要的组件,功能也比较明了。
     * 如果已经阅读过Controller组件,会发现Controller组件的代码也只有十来行,但它却可以做很多事,一定程度上
     * 要归功于Loader组件这个好助手或者好基友。
     * 不过Loader组件的代码真的不少,主要以常用的几个方法以主线来探讨:model(),view(),library(),helper();
     */
    class CI_Loader
    {
        //输出缓冲机制的嵌套级别
        protected $_ci_ob_level;
        //加载视图的路径列表
        protected $_ci_view_paths = array(VIEWPATH => TRUE);
        //加载库的路径列表
        protected $_ci_library_paths = array(APPPATH, BASEPATH);
        //加载模型的路径列表
        protected $_ci_model_paths = array(APPPATH);
        //加载助手的路径列表
        protected $_ci_helper_paths = array(APPPATH, BASEPATH);
        //缓存变量列表
        protected $_ci_cached_vars = array();
        //加载类列表
        protected $_ci_classes = array();
        //加载模型列表
        protected $_ci_models = array();
        //加载助手列表
        protected $_ci_helpers = array();
        //类名称映射列表
        protected $_ci_varmap = array(
            'unit_test' => 'unit',
            'user_agent' => 'agent'
        );
    
        /**
         * 构造函数
         * 设置组件加载路径,获取初始输出缓冲级。
         */
        public function __construct()
        {
            //获取初始输出缓冲级
            $this->_ci_ob_level = ob_get_level();
            //设置组件加载路径
            $this->_ci_classes =& is_loaded();
            log_message('info', 'Loader Class Initialized');
        }
    
        /**
         * Initializer 初始化
         */
        public function initialize()
        {
            //注意这里,看方法的名字,也可以猜到是对autoload的处理
            //加载autoload.php配置中文件
            $this->_ci_autoloader();
        }
    
        /**
         * 检测类是否加载
         */
        public function is_loaded($class)
        {
            return array_search(ucfirst($class), $this->_ci_classes, TRUE);
        }
    
        /**
         * 该方法用于加载核心类。
         * $library为相应的类名,$params为实例化此类的时候可能要用到的参数,
         * $object_name为给这个类的实例自义定一个名字。
         */
        public function library($library, $params = NULL, $object_name = NULL)
        {
            //接下来两个if都是关于合法性的判断。
            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;
        }
    
        /**
         * 加载模型
         * 加载过程和上面一个方法基本一样
         */
        public function model($model, $name = '', $db_conn = FALSE)
        {
            //可以以数组形式同时加载多个$model
            if (empty($model)) {
                return $this;
            } elseif (is_array($model)) {
                foreach ($model as $key => $value) {
                    is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
                }
                return $this;
            }
            $path = '';
    
            //判断该模型在一个子文件夹中吗?如果是这样,解析文件名和路径。
            if (($last_slash = strrpos($model, '/')) !== FALSE) {
                $path = substr($model, 0, ++$last_slash);
                $model = substr($model, $last_slash);
            }
            //如果没有给当前model定义名字,则以$model本身作为名字。
            if (empty($name)) {
                $name = $model;
            }
            //如果已经加载过此model,直接退出本函数。
            if (in_array($name, $this->_ci_models, TRUE)) {
                return $this;
            }
            $CI =& get_instance();
            //如果加载的model名字与之前加载过的类有冲突,则报错。
            if (isset($CI->$name)) {
                throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: ' . $name);
            }
            //如果要求同时连接数据库。则调用Loader::database()方法加载数据库类。
            if ($db_conn !== FALSE && !class_exists('CI_DB', FALSE)) {
                if ($db_conn === TRUE) {
                    $db_conn = '';
                }
                $this->database($db_conn, FALSE, TRUE);
            }
    
            //加载父类model。
            if (!class_exists('CI_Model', FALSE)) {
                $app_path = APPPATH . 'core' . DIRECTORY_SEPARATOR;
                if (file_exists($app_path . 'Model.php')) {
                    require_once($app_path . 'Model.php');
                    if (!class_exists('CI_Model', FALSE)) {
                        throw new RuntimeException($app_path . "Model.php exists, but doesn't declare class CI_Model");
                    }
                } elseif (!class_exists('CI_Model', FALSE)) {
                    require_once(BASEPATH . 'core' . DIRECTORY_SEPARATOR . 'Model.php');
                }
                //引入当前model
                $class = config_item('subclass_prefix') . 'Model';
                if (file_exists($app_path . $class . '.php')) {
                    require_once($app_path . $class . '.php');
                    if (!class_exists($class, FALSE)) {
                        throw new RuntimeException($app_path . $class . ".php exists, but doesn't declare class " . $class);
                    }
                }
            }
            //把文件名的第一个字母大写作为类名,规定的命名规范。
            $model = ucfirst($model);
            if (!class_exists($model, FALSE)) {
                foreach ($this->_ci_model_paths as $mod_path) {
                    if (!file_exists($mod_path . 'models/' . $path . $model . '.php')) {
                        continue;
                    }
                    require_once($mod_path . 'models/' . $path . $model . '.php');
                    if (!class_exists($model, FALSE)) {
                        throw new RuntimeException($mod_path . "models/" . $path . $model . ".php exists, but doesn't declare class " . $model);
                    }
                    break;
                }
                if (!class_exists($model, FALSE)) {
                    throw new RuntimeException('Unable to locate the model you have specified: ' . $model);
                }
            } elseif (!is_subclass_of($model, 'CI_Model')) {
                throw new RuntimeException("Class " . $model . " already exists and doesn't extend CI_Model");
            }
            //保存在Loader::_ci_models中,以后可以用它来判断某个model是否已经加载过。
            $this->_ci_models[] = $name;
            $CI->$name = new $model();
            return $this;
        }
    
        /**
         * 数据库Loader
         */
        public function database($params = '', $return = FALSE, $query_builder = NULL)
        {
            // Grab the super object
            $CI =& get_instance();
            //是否需要加载db
            if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && !empty($CI->db->conn_id)) {
                return FALSE;
            }
            require_once(BASEPATH . 'database/DB.php');
            if ($return === TRUE) {
                return DB($params, $query_builder);
            }
            $CI->db = '';
            // Load the DB class
            $CI->db =& DB($params, $query_builder);
            return $this;
        }
    
        /**
         * 加载数据库工具类
         */
        public function dbutil($db = NULL, $return = FALSE)
        {
            $CI =& get_instance();
            if (!is_object($db) OR !($db instanceof CI_DB)) {
                class_exists('CI_DB', FALSE) OR $this->database();
                $db =& $CI->db;
            }
            require_once(BASEPATH . 'database/DB_utility.php');
            require_once(BASEPATH . 'database/drivers/' . $db->dbdriver . '/' . $db->dbdriver . '_utility.php');
            $class = 'CI_DB_' . $db->dbdriver . '_utility';
            if ($return === TRUE) {
                return new $class($db);
            }
            $CI->dbutil = new $class($db);
            return $this;
        }
    
        /**
         * 加载数据库伪造类
         */
        public function dbforge($db = NULL, $return = FALSE)
        {
            $CI =& get_instance();
            if (!is_object($db) OR !($db instanceof CI_DB)) {
                class_exists('CI_DB', FALSE) OR $this->database();
                $db =& $CI->db;
            }
            require_once(BASEPATH . 'database/DB_forge.php');
            require_once(BASEPATH . 'database/drivers/' . $db->dbdriver . '/' . $db->dbdriver . '_forge.php');
            if (!empty($db->subdriver)) {
                $driver_path = BASEPATH . 'database/drivers/' . $db->dbdriver . '/subdrivers/' . $db->dbdriver . '_' . $db->subdriver . '_forge.php';
                if (file_exists($driver_path)) {
                    require_once($driver_path);
                    $class = 'CI_DB_' . $db->dbdriver . '_' . $db->subdriver . '_forge';
                }
            } else {
                $class = 'CI_DB_' . $db->dbdriver . '_forge';
            }
            if ($return === TRUE) {
                return new $class($db);
            }
            $CI->dbforge = new $class($db);
            return $this;
        }
    
        /**
         * 加载视图文件
         *  Loader::view();方法可以和Loader::file()方法一并来阅读,
         * 实质上它们都是调用了Loader::_ci_load();方法。
         */
        public 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));
        }
    
        /**
         * 加载普通文件
         */
        public function file($path, $return = FALSE)
        {
            return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
        }
    
        /**
         * 设置变量
         */
        public function vars($vars, $val = '')
        {
            if (is_string($vars)) {
                $vars = array($vars => $val);
            }
            $vars = $this->_ci_object_to_array($vars);
            if (is_array($vars) && count($vars) > 0) {
                foreach ($vars as $key => $val) {
                    $this->_ci_cached_vars[$key] = $val;
                }
            }
            return $this;
        }
    
        /**
         * 清除变量
         */
        public function clear_vars()
        {
            $this->_ci_cached_vars = array();
            return $this;
        }
    
        /**
         * 检查并获取变量
         */
        public function get_var($key)
        {
            return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
        }
    
        /**
         * 获取变量
         * 检索所有加载的变量。
         */
        public function get_vars()
        {
            return $this->_ci_cached_vars;
        }
    
        /**
         * 加载helper
         */
        public function helper($helpers = array())
        {
            //Loader::_ci_prep_filename()方法只是处理文件名,以返回正确的数组而已。
            //默认helper的文件名是以_helper为后缀,所以参数可以不用写_helper后缀,当然写也不会出错,因为
            //Loader::_ci_prep_filename()会帮你处理掉。
            foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) {
                if (isset($this->_ci_helpers[$helper])) {
                    continue;
                }
                //如果是扩展helper的话
                $ext_helper = config_item('subclass_prefix') . $helper;
                $ext_loaded = FALSE;
                foreach ($this->_ci_helper_paths as $path) {
                    if (file_exists($path . 'helpers/' . $ext_helper . '.php')) {
                        include_once($path . 'helpers/' . $ext_helper . '.php');
                        $ext_loaded = TRUE;
                    }
                }
                //如果我们加载了扩展-检查是否在这里
                if ($ext_loaded === TRUE) {
                    $base_helper = BASEPATH . 'helpers/' . $helper . '.php';
                    if (!file_exists($base_helper)) {
                        show_error('Unable to load the requested file: helpers/' . $helper . '.php');
                    }
                    include_once($base_helper);
                    $this->_ci_helpers[$helper] = TRUE;
                    log_message('info', 'Helper loaded: ' . $helper);
                    continue;
                }
                //如果没有扩展的话,则分别从APPPATH和BASEPATH,即应用目录和系统目录下找到相应的helper。
                //Loader::_ci_helper_paths默认是APPPATH和BASEPATH两个目录。如果你要再添加新的目录路径,可以
                //通过Loader::add_package_path()方法设置(model,library等同理)
                foreach ($this->_ci_helper_paths as $path) {
                    if (file_exists($path . 'helpers/' . $helper . '.php')) {
                        include_once($path . 'helpers/' . $helper . '.php');
                        $this->_ci_helpers[$helper] = TRUE;
                        log_message('info', 'Helper loaded: ' . $helper);
                        break;
                    }
                }
                //无法加载助手
                //如果该helper还没加载成功的话,说明加载helper失败
                if (!isset($this->_ci_helpers[$helper])) {
                    show_error('Unable to load the requested file: helpers/' . $helper . '.php');
                }
            }
            return $this;
        }
    
        /**
         * 可以看到helpers调用也是上面的helper,
         * 只是helpers的别名而已
         */
        public function helpers($helpers = array())
        {
            return $this->helper($helpers);
        }
    
        /**
         * 加载language文件
         */
        public function language($files, $lang = '')
        {
            get_instance()->lang->load($files, $lang);
            return $this;
        }
    
        /**
         * 加载配置文件
         * 这里的config方法,实质是完完全全调用Config组件的load方法而已。
         */
        public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
        {
            return get_instance()->config->load($file, $use_sections, $fail_gracefully);
        }
    
        /**
         * 加载 driver library
         */
        public function driver($library, $params = NULL, $object_name = NULL)
        {
            if (is_array($library)) {
                foreach ($library as $key => $value) {
                    if (is_int($key)) {
                        $this->driver($value, $params);
                    } else {
                        $this->driver($key, $params, $value);
                    }
                }
                return $this;
            } elseif (empty($library)) {
                return FALSE;
            }
            if (!class_exists('CI_Driver_Library', FALSE)) {
                //我们没有实例化对象,加载
                require BASEPATH . 'libraries/Driver.php';
            }
            if (!strpos($library, '/')) {
                $library = ucfirst($library) . '/' . $library;
            }
            return $this->library($library, $params, $object_name);
        }
    
        /**
         * 添加 Package 路径
         * 把package路径添加到库,模型,助手,配置路径
         */
        public function add_package_path($path, $view_cascade = TRUE)
        {
            $path = rtrim($path, '/') . '/';
            array_unshift($this->_ci_library_paths, $path);
            array_unshift($this->_ci_model_paths, $path);
            array_unshift($this->_ci_helper_paths, $path);
            $this->_ci_view_paths = array($path . 'views/' => $view_cascade) + $this->_ci_view_paths;
            $config =& $this->_ci_get_component('config');
            $config->_config_paths[] = $path;
            return $this;
        }
    
        /**
         * 获取Package Paths,默认不包含BASEPATH
         */
        public function get_package_paths($include_base = FALSE)
        {
            return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
        }
    
        /**
         * 剔除Package Path
         */
        public function remove_package_path($path = '')
        {
            $config =& $this->_ci_get_component('config');
            if ($path === '') {
                array_shift($this->_ci_library_paths);
                array_shift($this->_ci_model_paths);
                array_shift($this->_ci_helper_paths);
                array_shift($this->_ci_view_paths);
                array_pop($config->_config_paths);
            } else {
                $path = rtrim($path, '/') . '/';
                foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) {
                    if (($key = array_search($path, $this->{$var})) !== FALSE) {
                        unset($this->{$var}[$key]);
                    }
                }
                if (isset($this->_ci_view_paths[$path . 'views/'])) {
                    unset($this->_ci_view_paths[$path . 'views/']);
                }
                if (($key = array_search($path, $config->_config_paths)) !== FALSE) {
                    unset($config->_config_paths[$key]);
                }
            }
            //确保应用程序的默认路径仍然在数组中
            $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
            $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
            $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
            $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH . 'views/' => TRUE));
            $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
            return $this;
        }
    
        /**
         * Loader
         */
        protected function _ci_load($_ci_data)
        {
            //这里相当于把数组里面的元素拆开成变量。
            foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) {
                $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
            }
            $file_exists = FALSE;
            //当Loader::_ci_load()方法是通过Loader::file()调用的时候,则会有$_ci_path的值,如果是
            //如果Loader::view()调用的话,则有$_ci_view的值。
            //如果$_ci_path不为空,则说明当前要加载普通文件。
            if (is_string($_ci_path) && $_ci_path !== '') {
                //普通文件。这里只是获得文件名,以便找不到报错时候只报文件名而已。
                $_ci_x = explode('/', $_ci_path);
                $_ci_file = end($_ci_x);
            } else {
                //视图文件。
                //下面两行操作也是为了让外部可以通过xxx.php或者直接xxx的方式进行传参而已。
                $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
                $_ci_file = ($_ci_ext === '') ? $_ci_view . '.php' : $_ci_view;
                foreach ($this->_ci_view_paths as $_ci_view_file => $cascade) {
                    //从Loader::$_ci_view_paths中遍历视图文件,如果找到则退出。
                    //(默认仅有APPPATH/view/下,当然也可以通过Loader::add_package()方法设置)
                    if (file_exists($_ci_view_file . $_ci_file)) {
                        $_ci_path = $_ci_view_file . $_ci_file;
                        $file_exists = TRUE;
                        break;
                    }
                    //如果没有找到,会根据这个$cascade判断允不允许继续往下一个路径寻找视图文件。
                    if (!$cascade) {
                        break;
                    }
                }
            }
            //如果找不到文件(普通或视图都一样),则报错。
            if (!$file_exists && !file_exists($_ci_path)) {
                show_error('Unable to load the requested file: ' . $_ci_file);
            }
    
            //下面这个也很关键,其实视图文件里面的代码都是在属于Loader组件的,什么意思?
            //你可以随便写一个视图文件,然后在里面写上var_dump($this);可以发现,这个$this,是指Loader。
            //为什么会这样子呢?再往下面十几行代码的地方就说明了这一点。
            //这里是把CI所有的属性都开放给Loader组件用,这样在视图文件里面就可以通过$this->xxx的方式调用控制器
            //所有的东西。
            $_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;
                }
            }
            //在这里把在控制器里面通过$this->load->view("xxx",$data);中的$data解开,这就是为什么可以在视图文件
            //中可以用$data里面的变量的原因。其实还可以通过Loader::vars()方法,设置这些变量,它们会首先保存在
            //Loader::$_ci_cached_vars中
            if (is_array($_ci_vars)) {
                foreach (array_keys($_ci_vars) as $key) {
                    if (strncmp($key, '_ci_', 4) === 0) {
                        unset($_ci_vars[$key]);
                    }
                }
                $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
            }
            extract($this->_ci_cached_vars);
            //我们在控制器中调用$this->load->view()方法,
            //实质视图并没有马上输出来,而是先将它放到缓冲区。
            ob_start();
            //就是这个地方,下面if中有一句eval(xxxx)以及else中有include;而里面的xxxx正是我们要加载的视图文件,
            //所以这就是为什么在视图文件里,var_dump($this),会告诉你当前这个$this是Loader组件,因为视图的代码都是相当于
            //嵌入这个地方。
            if (!is_php('5.4') && !ini_get('short_open_tag') && 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('info', 'File loaded: ' . $_ci_path);
    
            //一般情况下,$_ci_return都为FLASE,即不要求通过$this->load->view()返回输出内容,
            //而是直接放到缓冲区静候处理;
            //当然你也可以先拿出数据,在控制器里面处理一下,再输出,例如在控制器中
            //$output=$this->load->view("x",$data,TRUE);,当为TRUE的时候,
            //下面的代码就起作用了。
            if ($_ci_return === TRUE) {
                $buffer = ob_get_contents();
                @ob_end_clean();
                return $buffer;
            }
            //下面这个很关键,因为有可能当前这个视图文件是被另一个视图文件通过$this->view()方法引入,即视图文件嵌入视图文件
            //从而导致多了一层缓冲。
            //为了保证缓冲内容最后交给Output处理时,缓冲级别只比Loader组件加载时多1(这个1就是最父层的视图文件引起的)
            //这里必须先flush掉当前层视图引起的这次缓冲,以保证Output正常工作。
            if (ob_get_level() > $this->_ci_ob_level + 1) {
                ob_end_flush();
            } else {
                //如果不是多1,则说明当前引入的视图文件就是直接在控制器里面引入的那个,
                //而不是由某个视图文件再引入的。
                //把缓冲区的内容交给Output组件并清空关闭缓冲区。
                $_ci_CI->output->append_output(ob_get_contents());
                @ob_end_clean();
            }
            return $this;
        }
    
        /**
         * 加载类
         */
        protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
        {
            //去掉后缀.php,是为了方便外部可以通过xxx.php也可以通过xxx.php来传入类名。
            //同时去掉两端的/。
            $class = str_replace('.php', '', trim($class, '/'));
            //因为CI允许通过"dir1/dir2/classname"的格式来组织和加载类,
            //所以还要判断类名中是否包括这些目录信息。
            if (($last_slash = strrpos($class, '/')) !== FALSE) {
                //目录部分
                $subdir = substr($class, 0, ++$last_slash);
                //类名部分
                $class = substr($class, $last_slash);
            } else {
                $subdir = '';
            }
            $class = ucfirst($class);
            //CI允许类文件以大写字母开头或者全小写,下面的遍历,就是在遍历这两种情况。
            //是否有我们开发人员自己写的扩展当前类的扩展?如果有的话,把它加载进来。
            if (file_exists(BASEPATH . 'libraries/' . $subdir . $class . '.php')) {
                return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
            }
    
            foreach ($this->_ci_library_paths as $path) {
                if ($path === BASEPATH) {
                    continue;
                }
                $filepath = $path . 'libraries/' . $subdir . $class . '.php';
                if (class_exists($class, FALSE)) {
                    //我们加载的类最终都是加载给超级控制器的,
                    //如果超级控制器已经有的话,那么我们没必要加载。
                    //如果没有,则实例它并加载给控制器。
                    if ($object_name !== NULL) {
                        $CI =& get_instance();
                        if (!isset($CI->$object_name)) {
                            return $this->_ci_init_library($class, '', $params, $object_name);
                        }
                    }
                    log_message('debug', $class . ' class already loaded. Second attempt ignored.');
                    return;
                }
                elseif (!file_exists($filepath)) {
                    continue;
                }
                include_once($filepath);
                return $this->_ci_init_library($class, '', $params, $object_name);
            }
            if ($subdir === '') {
                return $this->_ci_load_library($class . '/' . $class, $params, $object_name);
            }
            log_message('error', 'Unable to load the requested class: ' . $class);
            show_error('Unable to load the requested class: ' . $class);
        }
    
        /**
         * 库装载
         */
        protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
        {
            $prefix = 'CI_';
            if (class_exists($prefix . $library_name, FALSE)) {
                if (class_exists(config_item('subclass_prefix') . $library_name, FALSE)) {
                    $prefix = config_item('subclass_prefix');
                }
                if ($object_name !== NULL) {
                    $CI =& get_instance();
                    if (!isset($CI->$object_name)) {
                        return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
                    }
                }
                log_message('debug', $library_name . ' class already loaded. Second attempt ignored.');
                return;
            }
            $paths = $this->_ci_library_paths;
            array_pop($paths); // BASEPATH
            array_pop($paths); // APPPATH (needs to be the first path checked)
            array_unshift($paths, APPPATH);
            foreach ($paths as $path) {
                if (file_exists($path = $path . 'libraries/' . $file_path . $library_name . '.php')) {
                    include_once($path);
                    if (class_exists($prefix . $library_name, FALSE)) {
                        return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
                    } else {
                        log_message('debug', $path . ' exists, but does not declare ' . $prefix . $library_name);
                    }
                }
            }
            include_once(BASEPATH . 'libraries/' . $file_path . $library_name . '.php');
            $subclass = config_item('subclass_prefix') . $library_name;
            foreach ($paths as $path) {
                if (file_exists($path = $path . 'libraries/' . $file_path . $subclass . '.php')) {
                    include_once($path);
                    if (class_exists($subclass, FALSE)) {
                        $prefix = config_item('subclass_prefix');
                        break;
                    } else {
                        log_message('debug', $path . ' exists, but does not declare ' . $subclass);
                    }
                }
            }
            return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
        }
    
        /**
         * 此方法是用于实例化已经把类文件include进来的类
         */
        protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
        {
            //是否有类的配置信息
            if ($config === NULL) {
                //获取包含任何包路径的配置路径
                $config_component = $this->_ci_get_component('config');
                if (is_array($config_component->_config_paths)) {
                    $found = FALSE;
                    foreach ($config_component->_config_paths as $path) {
                        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;
                        }
                        if ($found === TRUE) {
                            break;
                        }
                    }
                }
            }
            $class_name = $prefix . $class;
            if (!class_exists($class_name, FALSE)) {
                log_message('error', 'Non-existent class: ' . $class_name);
                show_error('Non-existent class: ' . $class_name);
            }
            if (empty($object_name)) {
                $object_name = strtolower($class);
                if (isset($this->_ci_varmap[$object_name])) {
                    $object_name = $this->_ci_varmap[$object_name];
                }
            }
            $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.");
            }
            $this->_ci_classes[$object_name] = $class;
            $CI->$object_name = isset($config) ? new $class_name($config) : new $class_name();
        }
    
        /**
         * 自动加载器
         * autoload.php配置的自动加载文件有:
         * 1. Packages
         * 2. Libraries
         * 3. Helper files
         * 4. Custom config files
         * 5. Language files
         * 6. Models
         */
        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;
            }
            // 自动加载packages,也就是将package_path加入到library,model,helper,config
            if (isset($autoload['packages'])) {
                foreach ($autoload['packages'] as $package_path) {
                    $this->add_package_path($package_path);
                }
            }
            //加载config文件
            if (count($autoload['config']) > 0) {
                foreach ($autoload['config'] as $val) {
                    $this->config($val);
                }
            }
            //加载helper和language
            foreach (array('helper', 'language') as $type) {
                if (isset($autoload[$type]) && count($autoload[$type]) > 0) {
                    $this->$type($autoload[$type]);
                }
            }
            //加载drivers
            if (isset($autoload['drivers'])) {
                $this->driver($autoload['drivers']);
            }
            //加载libraries
            //这个好像是为了兼容以前版本的
            if (isset($autoload['libraries']) && count($autoload['libraries']) > 0) {
                if (in_array('database', $autoload['libraries'])) {
                    $this->database();
                    $autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
                }
                $this->library($autoload['libraries']);
            }
            //加载models
            if (isset($autoload['model'])) {
                $this->model($autoload['model']);
            }
        }
    
        /**
         * 返回由对象属性组成的关联数组
         */
        protected function _ci_object_to_array($object)
        {
            return is_object($object) ? get_object_vars($object) : $object;
        }
    
        /**
         * 获取CI某个组件的实例
         */
        protected function &_ci_get_component($component)
        {
            $CI =& get_instance();
            return $CI->$component;
        }
    
        /**
         * 处理文件名,这个函数主要是返回正确文件名
         */
        protected function _ci_prep_filename($filename, $extension)
        {
            if (!is_array($filename)) {
                return array(strtolower(str_replace(array($extension, '.php'), '', $filename) . $extension));
            } else {
                foreach ($filename as $key => $val) {
                    $filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val) . $extension);
                }
                return $filename;
            }
        }
    
    }

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`vue-loader.conf.js` 是一个用于配置 Vue 单文件组件(vue-loader)的配置文件。它一般用于 Vue CLI 2.x 中的 webpack 配置。 在 Vue 项目中,`.vue` 文件是一种特殊的文件类型,包含了模板、脚本和样式等组件相关代码。为了能够在 webpack 构建过程中解析和处理这些 `.vue` 文件,我们需要使用 vue-loader。 `vue-loader.conf.js` 文件中的配置项会被用于 vue-loader过程,以定义如何解析和转换 `.vue` 文件中的各个部分。 下面是一个简单的 `vue-loader.conf.js` 示例: ```javascript // vue-loader.conf.js module.exports = { loaders: { css: 'vue-style-loader!css-loader', scss: 'vue-style-loader!css-loader!sass-loader' // 其他配置... } }; ``` 在上述示例中,我们定义了 `css` 和 `scss` 两个的配置。这些配置会告诉 vue-loader 如何处理 `.vue` 文件中的样式部分。 注意,以上示例中的配置是基于 webpack 1.x 的写法。在 webpack 2.x 或更高版本中,可以使用 `rules` 或 `module.rules` 来定义规则。例如: ```javascript // vue-loader.conf.js module.exports = { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] }, { test: /\.scss$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader' ] } // 其他规则... ] }; ``` 在上述示例中,我们使用 `rules` 定义了两个规则,分别用于处理 `.css` 和 `.scss` 文件。 `vue-loader.conf.js` 中的配置还可以包括其它一些选项,如预处理的配置、CSS Modules 的开启等。具体配置项可以参考 vue-loader 的文档。 总之,`vue-loader.conf.js` 是一个用于配置 vue-loader文件,用于定义如何解析和处理 Vue 单文件组件中的各个部分。在 Vue CLI 2.x 中,它被用作 webpack 配置文件的一部分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值