Shudan Wang

没有人不爱惜他的生命,但很少人珍视他的时间。

  • 主页
  • 随笔
所有文章 友链 关于我

Shudan Wang

没有人不爱惜他的生命,但很少人珍视他的时间。

  • 主页
  • 随笔

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

2018-03-28

前言

加载器,顾名思义,是用于加载元素的,加载的元素可以是库(类),视图文件,驱动器,辅助函数,模型或其他你自己的文件,该类由系统自动加载,无需手工加载。

CI加载器还支持包(Package)概念,应用程序包(Package)可以很便捷的将你的应用部署在一个独立的目录中,以实现自己整套的类库,模型,辅助函数,配置,文件和语言包。建议将这些应用程序包放置在application/third_party目录下,比如下面是一个名为“Foo Bar”的应用程序包目录的例子。

1
2
3
4
5
6
7
/application/third_party/foo_bar

config/
helpers/
language/
libraries/
models/

我们先来看下CI框架中加载器类的实现框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//Nesting level of the output buffering mechanism
protected $_ci_ob_level;
//List of paths to load views from
protected $_ci_view_paths = array(VIEWPATH => TRUE);
//List of paths to load libraries from
protected $_ci_library_paths = array(APPPATH, BASEPATH);
//List of paths to load models from
protected $_ci_model_paths = array(APPPATH);
//List of paths to load helpers from
protected $_ci_helper_paths = array(APPPATH, BASEPATH);
//List of cached variables
protected $_ci_cached_vars = array();
//List of loaded classes
protected $_ci_classes = array();
//List of loaded models
protected $_ci_models = array();
//List of loaded helpers
protected $_ci_helpers = array();
//List of class name mappings
protected $_ci_varmap = array(
'unit_test' => 'unit',
'user_agent' => 'agent'
);

public function __construct(){...}
public function initialize(){...}
protected function _ci_autoloader(){...}
//Is Loaded
public function is_loaded($class){...}

public function add_package_path($path, $view_cascade = TRUE){...}
public function get_package_paths($include_base = FALSE){...}
public function remove_package_path($path = ''){...}

public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE){...}
public function helper($helpers = array()){...}
public function language($files, $lang = ''){...}
public function driver($library, $params = NULL, $object_name = NULL){...}
public function database($params = '', $return = FALSE, $query_builder = NULL){...}

public function library($library, $params = NULL, $object_name = NULL){...}
protected function _ci_load_library($class, $params = NULL, $object_name = NULL){...}
protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name){...}
protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL){...}

public function model($model, $name = '', $db_conn = FALSE){...}
public function dbutil($db = NULL, $return = FALSE){...}
public function dbforge($db = NULL, $return = FALSE){...}

public function view($view, $vars = array(), $return = FALSE){...}
public function file($path, $return = FALSE){...}
protected function _ci_load($_ci_data){...}
protected function _ci_prepare_view_vars($vars)

构造函数 construct()

设置组件加载路径,获取初始输出缓冲级别。

1
2
3
4
5
6
7
8
9
10
11
public function __construct(){
$this->_ci_ob_level = ob_get_level();
$this->_ci_classes =& is_loaded();

log_message('info', 'Loader Class Initialized');
}

//Initializer Figure out a way to move this to the constructor without breaking *package_path*() methods.
public function initialize(){
$this->_ci_autoloader();
}

_ci_autoloader()处理加载config/autoload.php中列出的组件,他们的类别有:包,类库,辅助文件,语言包,用户自定义配置文件,模型等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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);
}
}
//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]);
}
}
//Load 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']);
}
}

检测类是否加载 is_loaded($class)

为了检测一个类是否已被加载到self::$_ci_classes数组,主要用于表单辅助函数_get_validation_object();

1
2
3
public function is_loaded($class){
return array_search(ucfirst($class), $this->_ci_classes, TRUE);
}

包路径函数簇:add_package_path($path, $view_cascade = TRUE),get_package_paths($include_base = FALSE),remove_package_path($path = ‘’)

无论应用程序包是为了实现什么样的目的,它都包含了属于自己的配置文件、 辅助函数、语言包、类库和模型。如果要在你的控制器里使用这些资源, 你首先需要告知加载器(Loader)从应用程序包加载资源,使用 add_package_path() 方法来添加包的路径。

add_package_path($path, $view_cascade = TRUE)

添加一个包路径,用于告诉加载器类使用给定的路径来加载后续请求的资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
// add config file path
$config =& $this->_ci_get_component('config');
$config->_config_paths[] = $path;

return $this;
}

/**
* CI Component getter, Get a reference to a specific library or model.
* @param string $component Component name
* @return bool
*/
protected function &_ci_get_component($component){
$CI =& get_instance();
return $CI->$component;
}

get_package_paths($include_base = FALSE)

返回当前所有可用的包路径。

1
2
3
4
5
6
7
/**
* @param bool $include_base Whether to include BASEPATH (default: FALSE)
* @return array
*/
public function get_package_paths($include_base = FALSE){
return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
}

remove_package_path($path = ‘’)

当你的控制器完成从应用程序包中读取资源,如果你还需要读取其他的应用程序包的资源, 你会希望删除当前使用的包路径来让加载器不再使用这个文件夹中的资源。 要删除最后一次使用的包路径,你可以直接不带参数的调用该方法。

或者你也可以删除一个特定的包路径,指定与之前使用 add_package_path() 方法时 所加载的包相同的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @param string $path Path to remove
* @return object
*/
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]);
}
}

//make sure the application defaut paths are still in the array
$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;
}

Config Loader config($file, $use_sections = FALSE, $fail_gracefully = FALSE)

Loads a config file (an alias for CI_Config::load()).

1
2
3
public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE){
return get_instance()->config->load($file, $use_sections, $fail_gracefully);
}

Helper Loader helper($helpers = array())

该方法用于加载辅助函数文件,其中 file_name 为加载的文件名,不带 _helper.php 后缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* Load Helpers
*
* An alias for the helper() method in case the developer has
* written the plural form of it.
*
* @param string|string[] $helpers Helper name(s)
* @return object
*/
public function helpers($helpers = array()){
return $this->helper($helpers);
}

public function helper($helpers = array()){
is_array($helpers) OR $helpers = array($helpers);
foreach ($helpers as &$helper){
$filename = basename($helper);
$filepath = ($filename === $helper) ? '' : substr($helper, 0, strlen($helper) - strlen($filename));
$filename = strtolower(preg_replace('#(_helper)?(\.php)?$#i', '', $filename)).'_helper';
$helper = $filepath.$filename;

if (isset($this->_ci_helpers[$helper])){
continue;
}
// Is this a helper extension request?
$ext_helper = config_item('subclass_prefix').$filename;
$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 we have loaded extensions - check if the base one is here
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;
}

//No extensions found ... try loading regular helpers and/or overrides
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;
}
}
// unable to load the helper
if ( ! isset($this->_ci_helpers[$helper])){
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
}
return $this;
}

Language Loader language($files, $lang = ‘’)

1
2
3
4
public function language($files, $lang = ''){
get_instance()->lang->load($files, $lang);
return $this;
}

Driver Loader driver($library, $params = NULL, $object_name = NULL)

该方法用于加载驱动器类,和 library() 方法非常相似,另外你可以同时加载多个驱动器,只需给 driver 方法传入一个包含所有要载入的驱动器名的数组即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)){
//We aren't instantiating an object here, just making the base class available
require BASEPATH.'libraries/Driver.php';
}
//We can save the loader some time since Drivers will *always* be in a subfolder,
//and typically identically named to the library
if (! strpos($library, '/')){
$library = ucfirst($library).'/'.$library;
}
return $this->library($library, $params, $object_name);
}

Database Loader database($params = ‘’, $return = FALSE, $query_builder = NULL)

该方法用于加载数据库类,有两个可选的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @param mixed $params Database configuration options
* @param bool $return Whether to return the database object
* @param bool $query_builder Whether to enable Query Builder
* (overrides the configuration setting)
*
* @return mixed
*/
public function database($params = '', $return = FALSE, $query_builder = NULL){
$CI =& get_instance();

//Do we even need to load the database class?
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);
}
// Initialize the db variable. Needed to prevent reference errors with some configurations
$CI->db = '';
$CI->db =& DB($params, $query_builder);
return $this;
}

加载核心类方法簇:ci_load_library($class, $params = NULL, $object_name = NULL),ci_load_stock_library($library_name, $file_path, $params, $object_name),ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)

类库文件可以被保存到主 libraries 目录的子目录下面,或者保存到个人的 application/libraries 目录下。要载入子目录下的文件,只需将路径(你可以随心所欲地将文件保存到多层的子目录下)包含进来就可以了,注意这里说的路径是指相对于 libraries 目录的路径。

该方法用于加载核心类,$library为相应类名,$params为实例化此类的时候可能要用到的参数,$object_name为给这个类的实例自定义一个名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}

ci_load_library($class, $params = NULL, $object_name = NULL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* @param string $class Class name to load
* @param mixed $params Optional parameters to pass to the class constructor
* @param string $object_name Optional object name to assign to
* @return void
*/
protected function _ci_load_library($class, $params = NULL, $object_name = NULL){
//去掉文件名后缀.php,及两端的/
$class = str_replace('.php', '', trim($class, '/'));
//判断类名中是否包括目录信息,因为CI允许通过"dir1/dir2/classname"的格式来组织和加载类
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);

//是否开发扩展了系统核心类
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);
}

foreach ($this->_ci_library_paths as $path){
// BASEPATH has already been checked for
if ($path === BASEPATH){
continue;
}

$filepath = $path.'libraries/'.$subdir.$class.'.php';
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);
}

log_message('error', 'Unable to load the requested class: '.$class);
show_error('Unable to load the requested class: '.$class);
}

ci_load_stock_library($library_name, $file_path, $params, $object_name)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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');
}
$property = $object_name;
if (empty($property)){
$property = strtolower($library_name);
isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
}

$CI = & get_instance();
if ( ! isset($CI->$property)){
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')){
//Override
include_once($path);
if (class_exists($prefix.$library_name, FALSE)){
return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}
log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
}
}
include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');

// Check for extension
$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;
}
log_message('debug', $path.' exists, but does not declare '.$subclass);
}
}

return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}

ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* Internal CI Library Instantiator
* @param string $class Class name
* @param string $prefix Class name prefix
* @param array|null|bool $config Optional configuration to pass to the class constructor:
* FALSE to skip;
* NULL to search in config paths;
* array containing configuration data
* @param string $object_name Optional object name to assign to
* @return void
*/
protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL){
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){
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];
}
}

//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.");
}

$this->_ci_classes[$object_name] = $class;
$CI->$object_name = isset($config)? new $class_name($config):new $class_name();
}

Load model model($model, $name = ‘’, $db_conn = FALSE)

该方法用于加载模型,如果你的模型位于子目录下,加载时将路径包含进来即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public function model($model, $name = '', $db_conn = FALSE){
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 = '';
// Is the model in a sub-folder? If so, parse out the filename and path.
if (($last_slash = strrpos($model, '/')) !== FALSE){
$path = substr($model, 0, ++$last_slash);
$model = substr($model, $last_slash);
}

if (empty($name)) $name = $model;
if (in_array($name, $this->_ci_models, TRUE)){
return $this;
}
$CI =& get_instance();
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");
}
log_message('info', 'CI_Model class loaded');
}elseif ( ! class_exists('CI_Model', FALSE)){
require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
}

$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);
}
log_message('info', config_item('subclass_prefix').'Model class loaded');
}
}

$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");
}

$this->_ci_models[] = $name;
$model = new $model();
$CI->$name = $model;
log_message('info', 'Model "'.get_class($model).'" initialized');
return $this;
}

dbutil($db = NULL, $return = FALSE)

该方法用于加载数据库工具类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}

dbforge($db = NULL, $return = FALSE)

该方法用于加载数据库工厂类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

Load view view($view, $vars = array(), $return = FALSE)

该方法用于加载你的视图文件。

1
2
3
public function view($view, $vars = array(), $return = FALSE){
return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_prepare_view_vars($vars), '_ci_return' => $return));
}

Load file ile($path, $return = FALSE)

这是一个通用的文件载入方法,在第一个参数中给出文件所在的路径和文件名, 将会打开并读取对应的文件。默认情况下,数据会被发送给浏览器, 就如同视图文件一样,但如果你将第二个参数设置为 TRUE , 那么数据就会以字符串的形式被返回,而不是发送给浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public function file($path, $return = FALSE){
return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
}

/**
* Internal CI Data Loader
* Used to load views and files.
* Variables are prefixed with _ci_ to avoid symbol collision with
* variables made available to view files.
*
* @param array $_ci_data Data to load
* @return object
*/
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;
//如果$_ci_path不为空,则说明当前要加载普通文件。
if (is_string($_ci_path) && $_ci_path !== ''){
$_ci_x = explode('/', $_ci_path);
$_ci_file = end($_ci_x);
}else{
//当前加载视图文件
$_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中遍历视图文件,如果找到则退出。
if (file_exists($_ci_view_file.$_ci_file)){
$_ci_path = $_ci_view_file.$_ci_file;
$file_exists = TRUE;
break;
}

if ( ! $cascade){
break;
}
}
}

if ( ! $file_exists && ! 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. $this->xxx in view_file
$_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.
*/
empty($_ci_vars) OR $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
extract($this->_ci_cached_vars);

// Buffer the output
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 ( ! 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);
}
log_message('info', 'File loaded: '.$_ci_path);
// 一般情况下,$_ci_return都为FLASE,即不要求通过$this->load->view()返回输出内容,而是直接放到缓冲区静候处理;
if ($_ci_return === TRUE){
$buffer = ob_get_contents();
@ob_end_clean();
return $buffer;
}
/*
* Flush the buffer... or buff the flusher?
*
* 因为当前视图文件被另一个视图文件通过$this->view()方法引入,从而导致多了一层缓冲;为了保证缓冲内容能被Output正确处理,当缓冲级别只比Loader组件加载时多1(这个1就是最父层的视图文件引起的)时,就先flush掉当前层视图引起的这次缓冲。
*/
if (ob_get_level() > $this->_ci_ob_level + 1){
ob_end_flush();
}else{
//将缓冲区内容交给Output组件并清空关闭缓冲区
$_ci_CI->output->append_output(ob_get_contents());
@ob_end_clean();
}

return $this;
}

总结

Loader是CI框架中组件加载的管理器,而Loader是在CI_Controller中被加载的:$this->load =& load_class('Loader', 'core'); $this->load->initialize();。在Loader的initialize的过程中对autoload做了相应的处理,当然并不是所有的类库都需要通过CI的autoload加载,因为该类库在框架初始化的时候就被加载,而不管你是不是需要使用该类库,这样实际上会有一定的性能损失。如果你的类库并不是所有应用都需要的,那么更好的方法是需要时再加载。在结束本文前我们来梳理下CI_Loader调用流程:

赏

谢谢你请我吃糖果

支付宝
微信
  • CI框架源码解析

扫一扫,分享到微信

微信分享二维码
简单粗暴的文件上传漏洞
CodeIgniter框架源码解析十五之模型类文件Model.php
  1. 1. 前言
  2. 2. 构造函数 construct()
  3. 3. 检测类是否加载 is_loaded($class)
  4. 4. 包路径函数簇:add_package_path($path, $view_cascade = TRUE),get_package_paths($include_base = FALSE),remove_package_path($path = ‘’)
    1. 4.1. add_package_path($path, $view_cascade = TRUE)
    2. 4.2. get_package_paths($include_base = FALSE)
    3. 4.3. remove_package_path($path = ‘’)
  5. 5. Config Loader config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
  6. 6. Helper Loader helper($helpers = array())
  7. 7. Language Loader language($files, $lang = ‘’)
  8. 8. Driver Loader driver($library, $params = NULL, $object_name = NULL)
  9. 9. Database Loader database($params = ‘’, $return = FALSE, $query_builder = NULL)
  10. 10. 加载核心类方法簇:ci_load_library($class, $params = NULL, $object_name = NULL),ci_load_stock_library($library_name, $file_path, $params, $object_name),ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
    1. 10.1. ci_load_library($class, $params = NULL, $object_name = NULL)
    2. 10.2. ci_load_stock_library($library_name, $file_path, $params, $object_name)
    3. 10.3. ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
  11. 11. Load model model($model, $name = ‘’, $db_conn = FALSE)
  12. 12. dbutil($db = NULL, $return = FALSE)
  13. 13. dbforge($db = NULL, $return = FALSE)
  14. 14. Load view view($view, $vars = array(), $return = FALSE)
  15. 15. Load file ile($path, $return = FALSE)
  16. 16. 总结
© 2023 Shudan Wang
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链
  • 关于我

tag:

  • CI框架源码解析
  • HTTP原理
  • 网络技术
  • Laravel
  • Linux基础篇
  • Linux之常用命令
  • php杂集
  • 随笔
  • PHP
  • 网络安全
  • GO
  • mysql
  • Apache
  • Git
  • SSH
  • V2Ray
  • 数据结构
  • HTML
  • Nginx
  • NoSQL
  • Docker
  • Memcached

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 廖雪峰
  • 阮一峰
  • 陈皓
很惭愧

只做了一点微小的工作
谢谢大家