CodeIgniter源码阅读笔记(6)——地址解析类URI.php
2018-02-26 17:56
781 查看
URI类的作用主要是处理地址字符串,将URI分成对应的片段保存到segments,路由类也主要是通过segments数组来获取上下文中的URI请求信息
在阅读这段源码的时候,我们主要关心的问题有
1.URI类是如何将地址字符串解析成对应片段?
2.解析后的对应片段保存到变量中是怎样的数据结构?
cli模式:调用_parse_argv()进行解析
根据uri_protocol这个配置属性决定使用哪个解析函数
默认REQUEST_URI,使用_parse_request_uri解析函数
QUERY_STRING,使用_parse_query_string解析函数
PATH_INFO或其他参数,都使用_parse_request_uri解析函数
这些解析函数会将地址解析成uri字符串,再由_set_uri_string函数将uri字符串解析成对应片段
我们先看_construct的源码,之后再看具体的解析函数是如何实现的:
这里回答一下开头的两个问题:
1.URI类是如何将地址字符串解析成对应片段?
答:URI类首先将URL字符串解析成URI字符串,URI字符串的格式则是我们已经非常熟悉的CI路由地址(查询字符串,SCRIPT_NAME,以及SCRIPT_NAME目录名不会出现在uri字符串中),然后再将URI字符串中的参数解析出来存储到segments数组中,这样就讲url解析成对应的片段了。
2.解析后的对应片段保存到变量中是怎样的数据结构?
答:解析出来的片段从下标1开始依次保存到segments数组中。
在阅读这段源码的时候,我们主要关心的问题有
1.URI类是如何将地址字符串解析成对应片段?
2.解析后的对应片段保存到变量中是怎样的数据结构?
1.__construct()构造函数
URI类在初始化的时候就会对地址进行解析,构造函数会根据不同的环境调用对应的解析函数,并保存解析结果。cli模式:调用_parse_argv()进行解析
根据uri_protocol这个配置属性决定使用哪个解析函数
默认REQUEST_URI,使用_parse_request_uri解析函数
QUERY_STRING,使用_parse_query_string解析函数
PATH_INFO或其他参数,都使用_parse_request_uri解析函数
这些解析函数会将地址解析成uri字符串,再由_set_uri_string函数将uri字符串解析成对应片段
我们先看_construct的源码,之后再看具体的解析函数是如何实现的:
public function __construct() { //加载配置文件 $this->config =& load_class('Config', 'core'); //enable_query_strings参数为true的时候,直接从_GET数组中取出对应片段,所以不 //需要解析 if (is_cli() OR $this->config->item('enable_query_strings') !== TRUE) { //判断URI是否合法的正则表达式 $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars'); //如果是命令行模式 if (is_cli()) { //_parse_argv()是实际进行解析的函数,解析完后结果返回给$uri $uri = $this->_parse_argv(); } else { //URI属性参数 $protocol = $this->config->item('uri_protocol'); //默认是REQUEST_URI empty($protocol) && $protocol = 'REQUEST_URI'; //根据不同的URI属性调用对应的解析函数,并将结果返回给$uri switch ($protocol) { case 'AUTO': // For BC purposes only case 'REQUEST_URI': $uri = $this->_parse_request_uri(); break; case 'QUERY_STRING': $uri = $this->_parse_query_string(); break; case 'PATH_INFO': default: $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : $this->_parse_request_uri(); break; } } //将解析的uri字符串保存到uri_string中,对应片段存到segments变量 $this->_set_uri_string($uri); } log_message('info', 'URI Class Initialized'); }
2.命令行模式下_parse_argv()解析函数
这个uri字符串解析函数,是从$_SERVER[‘argv’]中获取参数protected function _parse_argv() { //取出所有参数 $args = array_slice($_SERVER['argv'], 1); //以'/'连接参数,组成uri字符串 return $args ? implode('/', $args) : ''; }
3._parse_request_uri()解析函数
从$_SERVER[‘REQUEST_URI’]中获取参数,protected function _parse_request_uri() { //如果$_SERVER['REQUEST_URI']或$_SERVER['SCRIPT_NAME']为空直接返回空 if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) { return ''; } //获取URL的query,path $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); $query = isset($uri['query']) ? $uri['query'] : ''; $uri = isset($uri['path']) ? $uri['path'] : ''; //去掉uri字符串中脚本名即xxx.php开头的字符串部分和脚本名所在目录名开头的字符串部 //分 if (isset($_SERVER['SCRIPT_NAME'][0])) { if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) { $uri = (string) substr($uri,strlen($_SERVER['SCRIPT_NAME'])); } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) { $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); } } //当出现http://localhost.com/path?/test?key=val时,只将key=val作为get参 //数 if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); $uri = $query[0]; $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else { $_SERVER['QUERY_STRING'] = $query; } //将查询字符串解析到$_GET数组中 parse_str($_SERVER['QUERY_STRING'], $_GET); if ($uri === '/' OR $uri === '') { return '/'; } //删除多余的斜杠,返回uri字符串 return $this->_remove_relative_directory($uri); }
4._parse_query_string()解析函数
根据$_SERVER[‘QUERY_STRING’]的参数解析出Uri字符串protected function _parse_query_string() { //获取$_SERVER['QUERY_STRING']参数 $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); //之后的代码与_parse_request_uri()函数一样 if (trim($uri, '/') === '') { return ''; } elseif (strncmp($uri, '/', 1) === 0) { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; $uri = $uri[0]; } parse_str($_SERVER['QUERY_STRING'], $_GET); return $this->_remove_relative_directory($uri); }
5._remove_relative_directory()——去掉多余斜杠和相对路径符号
protected function _remove_relative_directory($uri) { //以'/'分割uri字符串并保存到$uris数组中,将'..'字符串去掉 $uris = array(); $tok = strtok($uri, '/'); while ($tok !== FALSE) { if (( ! empty($tok) OR $tok === '0') && $tok !== '..') { $uris[] = $tok; } $tok = strtok('/'); } //用'/'重新拼接数组 return implode('/', $uris); }
6.uri解析成对应分段——_set_uri_string()函数
前面解读的几个函数已经将url解析成uri字符串了,但是我们最终需要的是Uri字符串所对应的参数,这样才能根据uri参数路由到正确的位置,set_uri_string()函数的功能便是将uri字符串解析成对应分段protected function _set_uri_string($str) { //移除$str中的隐藏字符串和字符串两侧的'/',并将$str的值赋给$this->uri_string $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/'); if ($this->uri_string !== '') { //如果配置文件中配置了url后缀,则移除后缀 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; //将uri_string字符串以'/'分割成数组,并将数组的元素存储到segments数组中 //segments数组下标由1开始 foreach (explode('/', trim($this->uri_string, '/')) as $val) { $val = trim($val); //判断是否符合参数是否合法 $this->filter_uri($val); if ($val !== '') { $this->segments[] = $val; } } unset($this->segments[0]); } }
7.合法性保障——filter_uri函数
public function filter_uri(&$str) { //参数为空或者不符合uri正则表达式,则参数不合法,展示错误界面 if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'. (UTF8_ENABLED ? 'u' : ''), $str)) { show_error('The URI you submitted has disallowed characters. ', 400); } }
总结:
以上几个函数则是URI.php中的主要函数了,还有将uri建立关联数组,获取segments这些函数,我只阅读了一下主要函数的代码,那些有兴趣的时候再看吧。这里回答一下开头的两个问题:
1.URI类是如何将地址字符串解析成对应片段?
答:URI类首先将URL字符串解析成URI字符串,URI字符串的格式则是我们已经非常熟悉的CI路由地址(查询字符串,SCRIPT_NAME,以及SCRIPT_NAME目录名不会出现在uri字符串中),然后再将URI字符串中的参数解析出来存储到segments数组中,这样就讲url解析成对应的片段了。
2.解析后的对应片段保存到变量中是怎样的数据结构?
答:解析出来的片段从下标1开始依次保存到segments数组中。
相关文章推荐
- CodeIgniter框架源码笔记(5)——识别多种URI风格:地址解析类URI.php
- CodeIgniter框架源码笔记(6)——支持友好的URI地址:路由类Router.php
- CodeIgniter源码阅读笔记(4)——性能测试Benchmark.php
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
- CodeIgniter源码阅读笔记(3)——公共函数Common.php
- CI框架源码解析八之地址解析类文件URI.php
- CodeIgniter源码阅读URI.php中_fetch_uri_string()函数的解析
- CodeIgniter源码阅读(三)(URI.php)
- CodeIgniter源码阅读笔记(5)——钩子Hooks.php
- CodeIgniter源码阅读笔记(7)——路由类Router.php
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
- CodeIgniter源码阅读(二)(Router.php)
- ****CI框架源码阅读笔记7 配置管理组件 Config.php
- CodeIgniter源码阅读(四)(Loader.php)
- CI框架源码阅读笔记8 控制器Controller.php
- CI框架源码阅读笔记6 扩展钩子 Hook.php
- CI框架源码阅读笔记3 全局函数Common.php
- CI框架源码阅读笔记6 扩展钩子 Hook.php
- CI框架源码阅读笔记6 扩展钩子 Hook.php
- CodeIgniter源码阅读(6)Hook.php 扩展框架的实现原理