CodeIgniter源码阅读(四)(Loader.php)

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函数,后面我们再去看这个函数;

  • 设置变量和取得变量(varsget_var

    vars函数将字符串或者数组或者object先转换成array,然后从数组里取出存在_ci_cached_vars里,get_var就是从_ci_cached_vars中取出数据;
    这两个函数的效果应该和view传递数据的方式相同,好像用到的比较少;

  • helper、helpers、language、config、driver

    这几个函数放在一起说,这几个函数的形式和用法都是类似的;

    helperhelpers函数完全是一样的效果,因为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_pathsremove_package_paths的结构基本相似,一个是增加libiary、helper、views和config文件夹路径,一个是移除路径,只要用到的就是array_shiftarray_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函数的阅读与注解


    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的缓冲区!

这个类很重要,也不简单;虽然读了很久,却也不能把每句话都讲清楚,但会细读。

你可能感兴趣的:(PHP,CodeIgniter,loader)