Loader.php里的函数基本上是整个CI里我们在编写代码的时候最长用到的。我们在编写Cotrollers里最长用到的$this->load->view()
方法,在__construct
里常用的$this->load->helper(url)
,都是在这里定义好的。甚至包括,我们load一个model后可以直接使用$this->some_model
来调用,也和Loader类有莫大的关系。
当然我觉得这一部分也是最难去描述的,还是粗略描述一下吧。
-
这个类开始就定义了十几个变量,出来第一个是int类型,其他全都是数组。
第一个参数是用来标记输出状态的,这个到Output类的时候我们再去看(我也没有具体去找这个参数使用的地方,目前仅是从字面意思上猜测);
其余几个参数也很好理解,四个带paths字样的,是用于记录相应文件的路径的;其余的也都是用来存放类和变量等信息。具体的用途我们可以到函数里再进行分析
-
构造函数和initialize函数
构造函数里初始化了上面定义的变量。其中
_ci_ob_level
直接调用ob_get_level
函数,common类里的is_loaded
返回的结果被赋值给_base_classes
(就是在CI的核心模块加载前加载的基础类);接下来又重写了is_loaded
方法;避免类的重复加载; -
装在类库和model的函数
这两个函数都很常用;library这个函数的参数也可以是一个library名字的数组;最后调用
_ci_load_class
函数来加载类;同样model部分也可以以数组的形式加载多个;函数先判断是否包含目录信息,进行分解;检查是否给model定义了名字、是否已经加载了model,是否model的名字有冲突;
$model = strtolower($model);
==>model文件名需全是小写,接着检查文件是否存在;
如果第三个参数不是false,就表示需要连接数据库,就会调用接下来的database函数;
装载父类model,再装载model; -
装载数据库和数据库的辅助函数
model,database,dbutil,dbforge三个函数。
database==>用于手动连接数据库,但有时候我们可以在config里设置自动连接数据库,主要是调用database文件夹下的DB函数;
dbutil==>用于装载数据库工具类(utility类)的函数,这个类中有一些常用的辅助函数,如导出数据库的csv_from_result
函数;
dbforge==>用于装载数据库维护类(forge)的函数,这个类里有一些添加或删除数据库之类的函数;关于数据库的部分,后面看到的时候再具体去分析;
-
装载视图和文件的函数
都是调用了
_ci_load
函数,后面我们再去看这个函数; -
设置变量和取得变量(
vars
与get_var
)vars
函数将字符串或者数组或者object先转换成array,然后从数组里取出存在_ci_cached_vars
里,get_var
就是从_ci_cached_vars
中取出数据;
这两个函数的效果应该和view
传递数据的方式相同,好像用到的比较少; -
helper、helpers、language、config、driver
这几个函数放在一起说,这几个函数的形式和用法都是类似的;
helper
和helpers
函数完全是一样的效果,因为helpers直接调用heler的效果;我们可以看到load部分很多函数接受的参数都可以是数组或者字符串形式,而函数都会做判断然后进行处理;
值得一提的是helper函数会调用_ci_prep_filename
来解析文件名,查看这个函数之后可以看到对函数名是通过str_replace
来处理的,所以当编写一个helper文件的时候,_helper
的后缀并不是必须的;language
函数的结构很简单,就是将语言文件名的数组(如果是字符串先组装成数组)循环交给lang类的load
函数处理;至于lang类,看过去的时候再看吧;config
更简单,直接调用config类的load
函数,值得注意的是config默认参数是字符串;driver
函数具体用途我倒不是很明确,看这个函数就是先检查一下是否装载了CI_Driver_Library
类,没有的话就加载文件,然后调用library
函数来处理的; -
(add/get/remove)_package_paths
添加/取得/移除包路径;抛开结构不说,我觉得CI在文件和函数命名以及细节处理上是很值得开发者借鉴的;
add_package_paths
和remove_package_paths
的结构基本相似,一个是增加libiary、helper、views和config文件夹路径,一个是移除路径,只要用到的就是array_shift
和array_unshift
两个函数,remove_package_paths
里还用到了array_unique
来保证框架的默认路径不会被错误的移除;
get_package_paths
返回的是package的路径,默认情况下不包括框架的基础路径; -
_ci_load
、_ci_load_class
、_ci_init_class
_ci_load
是供view和file函数调用的,用来装载文件或者视图;这是一个很关键的函数;视图的装载,缓冲,输送变量等都与这个函数有关;函数的具体实现形式稍微有点复杂,以后再详细了解; 个人认为这是非常重要的一个函数,详细解读见下方;
_ci_load_class
用于装载类,供library
函数调用;并调用_ci_init_class
来实例化类;
这三个函数是需要细读并且需要仔细理解的; -
_ci_autoloader
装载congfig/autoload里设置的需要自动加载的类,在initialize里调用;
-
三个辅助函数
其中
_ci_prep_filename
是用来解析文件名,上文已经提到过。
PS:_ci_load
函数的阅读与注解
<!-- lang: php -->
protected function _ci_load($_ci_data)
{
//函数被调用时是已数组的形式传入参数;一共以下是个简直,现在循环;
foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
{
//通过可变变量的方式将四个键值作为变量名,检查是否存在相应的变量,
//存在就赋值,否则为false;
$$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
}
//`$file_exists`作为一个文件是否存在的标记;
$file_exists = FALSE;
// file函数调用`_ci_load`的时候传入的是路径,如果`_ci_path`不为空,
//就用explode函数分解,并获得数组的最后一个成员作为文件名;
if ($_ci_path != '')
{
$_ci_x = explode('/', $_ci_path);
$_ci_file = end($_ci_x);
}
else
{
//如果`_ci_path`为空,就获取`_ci_view`的扩展信息(这里的意思应该是说,
//如果你没有load file,那我就默认你是通过view函数来调用的)
$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
//有了以下这句话,不管你写不写文件的后缀都可以,人性化处理啊~~
$_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
foreach ($this->_ci_view_paths as $view_file => $cascade)
{
//到`view_path`下去找view文件,如果找到就将文件路径赋值给`_ci_path`
//并标记一下状态;
if (file_exists($view_file.$_ci_file))
{
$_ci_path = $view_file.$_ci_file;
$file_exists = TRUE;
break;
}
//根据之前的函数可知$cascade是个bool变量,会根据这个变量判断允不允许
//继续往下一个路径寻找视图文件;这个处理是什么意思呢?设定一个终极目录,
//找不到就不找了?
if ( ! $cascade)
{
break;
}
}
}
//没有找到文件或者view就报错;
if ( ! $file_exists && ! file_exists($_ci_path))
{
show_error('Unable to load the requested file: '.$_ci_file);
}
//以下几句话把CI所有的属性都开放给Loader组件用,所以在视图文件
//里面可以通过`$this->abc`的方式调用控制器里所有的东西。
$_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函数将数组变量拆解;
if (is_array($_ci_vars))
{
$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}
extract($this->_ci_cached_vars);
//打开输出缓冲;
ob_start();
//检查php.ini里的`short_open_tag`设置是否打开;没有的话就把你的输出做一下
//处理~~~很不错的处理;
if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
{
echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
}
else
{
//如果一切正常,那就直接include;
include($_ci_path);
}
//打个标记
log_message('debug', 'File loaded: '.$_ci_path);
// 要求返回内容的话,就调用用`ob_get_contents`函数,然后通过
//`ob_end_clean`丢弃最顶层输出缓冲区的内容并关闭这个缓冲区;
if ($_ci_return === TRUE)
{
$buffer = ob_get_contents();
@ob_end_clean();
return $buffer;
}
//这个判断是保证当视图文件嵌入视图文件的时候,多了一层缓冲,先flush掉
//当前层视图引起的这次缓冲,以保证Output正常工作;
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();
}
}
//要想彻底搞懂CI的输出机制和这个函数的实现,要好好学习一下PHP的缓冲区!
这个类很重要,也不简单;虽然读了很久,却也不能把每句话都讲清楚,但会细读。