资讯专栏INFORMATION COLUMN

CI框架源码解读--ROUTE和URL类

trilever / 2564人阅读

摘要:支持基于段方法和查询字符串方法两种形式的。里的方法就是利用类来实现解析出类名方法名。在类的构造函数里有一步方法代码如下如果你的原始是的话,经过这个方法处理,你会得到参考文章

路由的目的是为了从URL中解析出class类名是什么,method方法名是什么,所传的参数有哪些,参数值又是什么,类文件存在的路径是哪。最终实现方法的调度。

CI支持基于段方法和查询字符串方法两种形式的URL。

基于段形式:

example.com/news/article/my_article.html

查询字符串形式:

index.php?c=products&m=view&id=345

URL的获取方法有如下几种:PATH_INFO、QUERY_STRING、REQUEST_URI、ORIG_PATH_INFO。比较常用的是PATH_INFO。几种方式的差异可以简单通过打印$_SERVER来查看。比如xxx.com/welcome/test_search.html?c=welcome&d=test_search,打印的结果是(只挑了这几部分):

Array
(
    [QUERY_STRING] => c=welcome&d=test_search
    [REQUEST_URI] => /welcome/test_search.html?c=welcome&d=test_search
    [PATH_INFO] => /welcome/test_search.html
)

下面是源码config文件里关于这几种方法的定义。

/*
|--------------------------------------------------------------------------
| URI PROTOCOL
|--------------------------------------------------------------------------
|
| This item determines which server global should be used to retrieve the
| URI string.  The default setting of "AUTO" works for most servers.
| If your links do not seem to work, try one of the other delicious flavors:
|
| "AUTO"            Default - auto detects
| "PATH_INFO"       Uses the PATH_INFO
| "QUERY_STRING"    Uses the QUERY_STRING
| "REQUEST_URI"     Uses the REQUEST_URI
| "ORIG_PATH_INFO"  Uses the ORIG_PATH_INFO
|
*/
$config["uri_protocol"] = "PATH_INFO";

我们这里用的也是PATH_INFO来获取。

至此,我们就拥有了URL地址,接下来我们就要分析地址。URL类就是来做分析的事情。

system/core/Router.php里的_set_routing()方法就是利用URL类来实现解析出类名方法名。

看下代码(下面的代码都是CodeIgniter-3.0.6版本的),英文注释已经很详细了,我在关键点额外加了点中文注释:

/**
 * Set route mapping
 *
 * Determines what should be served based on the URI request,
 * as well as any "routes" that have been set in the routing config file.
 *
 * @return    void
 */
protected function _set_routing()
{
    // Load the routes.php file. It would be great if we could
    // skip this for enable_query_strings = TRUE, but then
    // default_controller would be empty ...
    if (file_exists(APPPATH."config/routes.php"))
    {
        include(APPPATH."config/routes.php");
    }

    if (file_exists(APPPATH."config/".ENVIRONMENT."/routes.php"))
    {
        include(APPPATH."config/".ENVIRONMENT."/routes.php");
    }

    // Validate & get reserved routes
    if (isset($route) && is_array($route))
    {
        isset($route["default_controller"]) && $this->default_controller = $route["default_controller"];
        isset($route["translate_uri_dashes"]) && $this->translate_uri_dashes = $route["translate_uri_dashes"];
        unset($route["default_controller"], $route["translate_uri_dashes"]);
        $this->routes = $route;
    }

    // Are query strings enabled in the config file? Normally CI doesn"t utilize query strings
    // since URI segments are more search-engine friendly, but they can optionally be used.
    // If this feature is enabled, we will gather the directory/class/method a little differently
    // 这段不用看,我们项目中是FALSE
    if ($this->enable_query_strings)
    {
        // If the directory is set at this time, it means an override exists, so skip the checks
        if ( ! isset($this->directory))
        {
            $_d = $this->config->item("directory_trigger");
            $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " 	

x0B/") : "";

            if ($_d !== "")
            {
                $this->uri->filter_uri($_d);
                $this->set_directory($_d);
            }
        }

        $_c = trim($this->config->item("controller_trigger"));
        if ( ! empty($_GET[$_c]))
        {
            $this->uri->filter_uri($_GET[$_c]);
            $this->set_class($_GET[$_c]);

            $_f = trim($this->config->item("function_trigger"));
            if ( ! empty($_GET[$_f]))
            {
                $this->uri->filter_uri($_GET[$_f]);
                $this->set_method($_GET[$_f]);
            }

            $this->uri->rsegments = array(
                1 => $this->class,
                2 => $this->method
            );
        }
        else
        {
            $this->_set_default_controller();
        }

        // Routing rules don"t apply to query strings and we don"t need to detect
        // directories, so we"re done here
        return;
    }

    // Is there anything to parse?
    //这里的$this->uri是route类构造函数里面$this->uri =& load_class("URI", "core");来的,后文我们会详细看URI类
    if ($this->uri->uri_string !== "")
    {
        //绝大多数情况是会走这里
        $this->_parse_routes();
    }
    else
    {
        $this->_set_default_controller();
    }

所以我们重点再看ROUTE的_parse_routes()方法:

/**
 * Parse Routes
 *
 * Matches any routes that may exist in the config/routes.php file
 * against the URI to determine if the class/method need to be remapped.
 *
 * @return    void
 */
protected function _parse_routes()
{
    // Turn the segment array into a URI string
    // $this->uri->segments我们后面也会讲到
    $uri = implode("/", $this->uri->segments);

    // Get HTTP verb
    $http_verb = isset($_SERVER["REQUEST_METHOD"]) ? strtolower($_SERVER["REQUEST_METHOD"]) : "cli";

    // Loop through the route array looking for wildcards
    // 查看是否符合config文件里的配置
    foreach ($this->routes as $key => $val)
    {
        // Check if route format is using HTTP verbs
        if (is_array($val))
        {
            $val = array_change_key_case($val, CASE_LOWER);
            if (isset($val[$http_verb]))
            {
                $val = $val[$http_verb];
            }
            else
            {
                continue;
            }
        }

        // Convert wildcards to RegEx
        $key = str_replace(array(":any", ":num"), array("[^/]+", "[0-9]+"), $key);

        // Does the RegEx match?
        if (preg_match("#^".$key."$#", $uri, $matches))
        {
            // Are we using callbacks to process back-references?
            if ( ! is_string($val) && is_callable($val))
            {
                // Remove the original string from the matches array.
                array_shift($matches);

                // Execute the callback using the values in matches as its parameters.
                $val = call_user_func_array($val, $matches);
            }
            // Are we using the default routing method for back-references?
            elseif (strpos($val, "$") !== FALSE && strpos($key, "(") !== FALSE)
            {
                $val = preg_replace("#^".$key."$#", $val, $uri);
            }

            $this->_set_request(explode("/", $val));
            return;
        }
    }

    // If we got this far it means we didn"t encounter a
    // matching route so we"ll set the site default route
    $this->_set_request(array_values($this->uri->segments));
}

再看_set_request()方法:

/**
 * Set request route
 *
 * Takes an array of URI segments as input and sets the class/method
 * to be called.
 *
 * @used-by    CI_Router::_parse_routes()
 * @param    array    $segments    URI segments
 * @return    void
 */
protected function _set_request($segments = array())
{
    $segments = $this->_validate_request($segments);
    // If we don"t have any segments left - try the default controller;
    // WARNING: Directories get shifted out of the segments array!
    if (empty($segments))
    {
        $this->_set_default_controller();
        return;
    }

    if ($this->translate_uri_dashes === TRUE)
    {
        $segments[0] = str_replace("-", "_", $segments[0]);
        if (isset($segments[1]))
        {
            $segments[1] = str_replace("-", "_", $segments[1]);
        }
    }

    //找到类名了
    $this->set_class($segments[0]);
    if (isset($segments[1]))
    {
        //找到方法名了
        $this->set_method($segments[1]);
    }
    else
    {
        $segments[1] = "index";
    }

    array_unshift($segments, NULL);
    unset($segments[0]);
    $this->uri->rsegments = $segments;


到这里,就根据URL找出了类名和方法名,在引导文件system/core/CodeIgniter.php里面就可以实现调度了。

$class = ucfirst($RTR->class);
$method = $RTR->method;

$CI = new $class();

/*
 * ------------------------------------------------------
 *  Call the requested method
 * ------------------------------------------------------
 */
call_user_func_array(array(&$CI, $method), $params);

下面我们看下URI类,来看看上面代码里的$this->uri等各项是怎么得到的。

在URI类的构造函数里有一步:

$this->_set_uri_string($uri)

_set_uri_string()方法代码如下:

/**
 * Set URI String
 *
 * @param     string    $str
 * @return    void
 */
protected function _set_uri_string($str)
{
    // Filter out control characters and trim slashes
    $this->uri_string = trim(remove_invisible_characters($str, FALSE), "/");

    if ($this->uri_string !== "")
    {
        // Remove the URL suffix, if present
        if (($suffix = (string) $this->config->item("url_suffix")) !== "")
        {
            $slen = strlen($suffix);

            if (substr($this->uri_string, -$slen) === $suffix)
            {
                $this->uri_string = substr($this->uri_string, 0, -$slen);
            }
        }

        $this->segments[0] = NULL;
        // Populate the segments array
        foreach (explode("/", trim($this->uri_string, "/")) as $val)
        {
            $val = trim($val);
            // Filter segments for security
            $this->filter_uri($val);

            if ($val !== "")
            {
                $this->segments[] = $val;
            }
        }

        unset($this->segments[0]);
    }
}

如果你的原始uri是http://example.com/index.php/news/local/metro/crime_is_up的话,经过这个_set_uri_string()方法处理,你会得到$this->segments = array([1]=>"news", [2]=>"local",[3]=>"metro",[4]=>"crime_is_up")

参考文章

http://codeigniter.org.cn/use...

http://codeigniter.org.cn/use...

http://codeigniter.org.cn/use...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21911.html

相关文章

  • CI3设置子目录控制器为默认控制器的解决办法

    摘要:在框架中配置文件多目录前后台应该是个很常见的事情。于是在求学问道的途中,终于得到了比较完美的解决方法。业务需求环境需求在中实现前后台的效果。因为已经不支持设置子目录下的控制器为默认控制器的功能。 showImg(https://segmentfault.com/img/remote/1460000010545771);     在框架中配置文件多目录、前后台应该是个很常见的事情。像一...

    niceforbear 评论0 收藏0
  • Backbone源码解读(三)

    摘要:事件关于路由触发事件是通过两个函数来完成的,它们分别是和前者会检测路由是否发生了改变,如果改变了就会触发函数并调用函数,而后者会通过路由片段来找到相关的事件函数来触发。 注意:强烈建议一边阅读源码一边阅读本文。 终于到了backbone源码解读的最后一篇,这一篇和前面几篇时间上有一定的间隔(因为要回学校有一堆乱七八糟的事...)。在这一篇里面会讲解Bakcbone的sync & rou...

    feng409 评论0 收藏0
  • Codeigniter 4.0-dev 版源码学习笔记之一——前言以及 CI 4 预览

    摘要:版权声明可转载,但不论任何媒体都需要在转载前与本人沟通,并在转载时注明出处。的各个核心模块以模块名为目录名分别存储在这个目录下。下一篇文章会涉及到和。此文可以转载,但转载前需要发邮件到进行沟通,未沟通的均视作侵权。 写在前面: 为什么选择开发过程中的 CI 4 作为源码解读版本:(1)首先我选 CI 是因为它之前的稳定版都是相对比较轻量小巧的,而且可以认为是简单的。(2)为什么没有选...

    MSchumi 评论0 收藏0
  • CI 框架简单入门笔记

    摘要:方法名不区分大小写。当时有面试官还问过,我以为所有的框架都是,真是汗颜的属性有等的超级对象类分段获取从入口文件后,分断获取是按照后面开始的第一个段开始。 1.mvc一般思路 C层: 入口文件-URL获取控制器$_GET[c]和方法$_GET[a]--包含c的文件类(控制器文件)- 实例化c控制器对象-调用方法a(并协调模型和视图),C层不直接调用DB,下面会有提到。 V层: 在C层中i...

    Kross 评论0 收藏0
  • Luthier CI 认证框架 Authentication Framework

    摘要:返回表示用户的对象。相反,存储使用单向加密算法生成的哈希。例删除当前会话要从当前身份验证会话中删除所有数据包括当前存储的经过身份验证的用户,请使用静态方法用户操作有两种操作可用于对经过身份验证的用户执行角色验证和权限验证。检查密码重置请求。 Luthier CI 认证框架 ( Authentication Framework ) 内容 Contents 介绍 Introduct...

    Elle 评论0 收藏0

发表评论

0条评论

trilever

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<