Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51133
  • 博文数量: 12
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 142
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-16 21:18
文章分类

全部博文(12)

文章存档

2015年(1)

2014年(1)

2013年(10)

我的朋友

分类: PHP

2013-09-20 20:10:27

     接下来分析一个我感觉比较重要的类,就是URI类,这个类负责的是URI的解析,其实也很简单,说白了就干了一件事:把各式各样的URI规则都可以解析出来,顺便过滤一下,然后可以有URI的重建功能,这里大量的引用了$_SERVER这个数组,其实想想也很简单,我们或许到的信息,不都是从这些数组里才能看到的嘛.
     这里只是负责解析,并没有把它们和控制器的方法的参数对应起来,但是提供了很好的接口,就是说对于一次确定的访问,它可以捕获到特定的段,这就够了.

点击(此处)折叠或打开

  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

  2. class CI_URI {
  3.     //缓存的URI查询数组.
  4.     var    $keyval            = array();
  5.     //uri查询字符串.
  6.     var $uri_string;
  7.     //uri段
  8.     var $segments        = array();
  9.     //uri重建之后的段
  10.     var $rsegments        = array();

  11.     
  12.      //构造函数,
  13.     function __construct()
  14.     {
  15.         $this->config =& load_class('Config', 'core');
  16.         log_message('debug', "URI Class Initialized");
  17.     }


  18.     //得到uri字符串,返回类型为字符串类型.
  19.     function _fetch_uri_string()
  20.     {
  21.         //如果uri是自由模式.
  22.         if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
  23.         {
  24.             //如果是cli请求,则使用解析cli的方式去解析它.
  25.             if (defined('STDIN'))
  26.             {
  27.                 $this->uri_string = $this->_parse_cli_args();
  28.                 return;
  29.             }

  30.             
  31.             //先调用查询字符串的方式来分析,这在大多数情况下都有效.
  32.             if ($uri = $this->_detect_uri())
  33.             {
  34.                 $this->uri_string = $uri;
  35.                 return;
  36.             }

  37.             //有些服务器使用getenv()会出现问题,因此我们用两种方式检测它.
  38.             //是否开启了pathinfo模式.
  39.             $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
  40.             if (trim($path, '/') != '' && $path != "/".SELF)
  41.             {
  42.                 $this->uri_string = $path;
  43.                 return;
  44.             }

  45.             
  46.             //没有开启pathinfo模式,那么QUERY_STRING呢?
  47.             $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
  48.             if (trim($path, '/') != '')
  49.             {
  50.                 $this->uri_string = $path;
  51.                 return;
  52.             }

  53.             //最后我们试试GET数组的方式.
  54.             if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
  55.             {
  56.                 $this->uri_string = key($_GET);
  57.                 return;
  58.             }

  59.             //我们测试了所有的可能,只能返回一个空字符串.
  60.             $this->uri_string = '';
  61.         }
  62.         else
  63.         {
  64.             //首先查看URI使用的方式
  65.             $uri = strtoupper($this->config->item('uri_protocol'));
  66.             //如果确定是查询字符串方式的话,交给_detect_uri()函数.
  67.             if ($uri == 'REQUEST_URI')
  68.             {
  69.                 $this->uri_string = $this->_detect_uri();
  70.                 return;
  71.             }
  72.             //如果是cli模式,交个_parse_cli_args()函数.
  73.             elseif ($uri == 'CLI')
  74.             {
  75.                 $this->uri_string = $this->_parse_cli_args();
  76.                 return;
  77.             }

  78.             $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
  79.         }

  80.         // 如果该uri只包含一个斜线,那么直接返回即可.
  81.         if ($this->uri_string == '/')
  82.         {
  83.             $this->uri_string = '';
  84.         }
  85.     }

  86.     
  87.      //探测URI,它会自动探测URI并且会修正查询字符串的格式.
  88.     private function _detect_uri()
  89.     {
  90.         //如果访问当前的URI为空,返回空字符串.
  91.         if ( ! isset($_SERVER['REQUEST_URI']))
  92.         {
  93.             return '';
  94.         }
  95.         //首先得到该uri
  96.         $uri = $_SERVER['REQUEST_URI'];
  97.         //把当前的uri和当前脚本的路径进行对比.
  98.         //这里要注意一个细节,那就是index.php或者index.html不用写到具体的文件名
  99.         //因此才会出现下面的dirname的情况.
  100.         //删除脚本名称,只保留后面的变量部分.
  101.         if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
  102.         {
  103.             $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
  104.             
  105.         }
  106.         elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
  107.         {
  108.             $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
  109.         }

  110.         //这部分确保即使在Nginx上也会成立.
  111.         //URI已经找到了,同时修改查询字符串和GET数组.
  112.         //如果包含"?/",则删掉它.
  113.         //这是因为我们允许?/welcom/index.php这种格式.
  114.         if (strncmp($uri, '?/', 2) === 0)
  115.         {
  116.             $uri = substr($uri, 2);
  117.         }
  118.         
  119.         $parts = preg_split('#\?#i', $uri, 2);
  120.         $uri = $parts[0];
  121.         //如果采用的是普通模式.
  122.         if (isset($parts[1]))
  123.         {
  124.             $_SERVER['QUERY_STRING'] = $parts[1];
  125.             //把查询字符串解析到GET数组中去.
  126.             parse_str($_SERVER['QUERY_STRING'], $_GET);
  127.         }
  128.         else
  129.         {
  130.             $_SERVER['QUERY_STRING'] = '';
  131.             $_GET = array();
  132.         }
  133.         
  134.         if ($uri == '/' || empty($uri))
  135.         {
  136.             return '/';
  137.         }
  138.         //解析url,        
  139.         $uri = parse_url($uri, PHP_URL_PATH);

  140.         // 在URI做一些清理并且返回它.
  141.         return str_replace(array('//', '../'), '/', trim($uri, '/'));
  142.     }

  143.     
  144.     
  145.      //解析cli参数,把每一个命令行参数并且假定它是URI的一个段.
  146.     private function _parse_cli_args()
  147.     {
  148.         $args = array_slice($_SERVER['argv'], 1);

  149.         return $args ? '/' . implode('/', $args) : '';
  150.     }

  151.     
  152.     
  153.      //过滤掉恶意的字符,参数类型为字符串,返回类型为字符串.
  154.     function _filter_uri($str)
  155.     {
  156.         if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
  157.         {
  158.             // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
  159.             // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
  160.             if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
  161.             {
  162.                 show_error('The URI you submitted has disallowed characters.', 400);
  163.             }
  164.         }

  165.         //把程序中的字符变成实体.
  166.         $bad    = array('$',        '(',        ')',        '%28',        '%29');
  167.         $good    = array('$',    '(',    ')',    '(',    ')');

  168.         return str_replace($bad, $good, $str);
  169.     }

  170.     

  171.     
  172.      //如果有必要,除掉URL的伪后缀名.
  173.     function _remove_url_suffix()
  174.     {
  175.         if ($this->config->item('url_suffix') != "")
  176.         {
  177.             $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
  178.         }
  179.     }

  180.     
  181.      //分解URI段,这个独立的段会被存储到该对象的segments数组中去.
  182.     function _explode_segments()
  183.     {
  184.         foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
  185.         {
  186.             // 为了安全起见,进行过滤.
  187.             $val = trim($this->_filter_uri($val));

  188.             if ($val != '')
  189.             {
  190.                 $this->segments[] = $val;
  191.             }
  192.         }
  193.     }

  194.     
  195.     
  196.      //对URI段进行重建索引,数组的索引从1开始而不是从0开始.
  197.     function _reindex_segments()
  198.     {
  199.         array_unshift($this->segments, NULL);
  200.         array_unshift($this->rsegments, NULL);
  201.         unset($this->segments[0]);
  202.         unset($this->rsegments[0]);
  203.     }

  204.     

  205.     
  206.      //获取URI中的一个段.参数为整型,返回类型为字符串.
  207.     function segment($n, $no_result = FALSE)
  208.     {
  209.         return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
  210.     }

  211.     
  212.     
  213.      //返回重建路由后的一个段.
  214.     function rsegment($n, $no_result = FALSE)
  215.     {
  216.         return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
  217.     }

  218.     //从URI字符串生成一个键值对.
  219.      //参数分别为起始段序号,默认的数组名,返回值为数组.
  220.     function uri_to_assoc($n = 3, $default = array())
  221.     {
  222.         return $this->_uri_to_assoc($n, $default, 'segment');
  223.     }
  224.     
  225.     //对于ruri实现同样的功能.
  226.     function ruri_to_assoc($n = 3, $default = array())
  227.     {
  228.         return $this->_uri_to_assoc($n, $default, 'rsegment');
  229.     }

  230.     
  231.      //从URI字符串或者重建URI之后的字符串生成键值对数组.
  232.      //参数第一个为起始段,第二个为默认值,第三个为使用的数组名.
  233.     function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
  234.     {
  235.         if ($which == 'segment')
  236.         {
  237.             $total_segments = 'total_segments';
  238.             $segment_array = 'segment_array';
  239.         }
  240.         else
  241.         {
  242.             $total_segments = 'total_rsegments';
  243.             $segment_array = 'rsegment_array';
  244.         }

  245.         if ( ! is_numeric($n))
  246.         {
  247.             return $default;
  248.         }

  249.         if (isset($this->keyval[$n]))
  250.         {
  251.             return $this->keyval[$n];
  252.         }

  253.         if ($this->$total_segments() < $n)
  254.         {
  255.             if (count($default) == 0)
  256.             {
  257.                 return array();
  258.             }

  259.             $retval = array();
  260.             foreach ($default as $val)
  261.             {
  262.                 $retval[$val] = FALSE;
  263.             }
  264.             return $retval;
  265.         }

  266.         $segments = array_slice($this->$segment_array(), ($n - 1));

  267.         $i = 0;
  268.         $lastval = '';
  269.         $retval = array();
  270.         foreach ($segments as $seg)
  271.         {
  272.             if ($i % 2)
  273.             {
  274.                 $retval[$lastval] = $seg;
  275.             }
  276.             else
  277.             {
  278.                 $retval[$seg] = FALSE;
  279.                 $lastval = $seg;
  280.             }

  281.             $i++;
  282.         }

  283.         if (count($default) > 0)
  284.         {
  285.             foreach ($default as $val)
  286.             {
  287.                 if ( ! array_key_exists($val, $retval))
  288.                 {
  289.                     $retval[$val] = FALSE;
  290.                 }
  291.             }
  292.         }

  293.         // 为了重用,缓存该数组.
  294.         $this->keyval[$n] = $retval;
  295.         return $retval;
  296.     }

  297.     
  298.      //从一个关联数组生成一个uri字符串.
  299.      //返回类型为一个数组,参数为一个关联数组.
  300.     function assoc_to_uri($array)
  301.     {
  302.         $temp = array();
  303.         foreach ((array)$array as $key => $val)
  304.         {
  305.             $temp[] = $key;
  306.             $temp[] = $val;
  307.         }

  308.         return implode('/', $temp);
  309.     }

  310.     //取得一个URI段并且加上斜线.
  311.     function slash_segment($n, $where = 'trailing')
  312.     {
  313.         return $this->_slash_segment($n, $where, 'segment');
  314.     }

  315.     //取得一个重建后的URI段并且加上斜线.
  316.     function slash_rsegment($n, $where = 'trailing')
  317.     {
  318.         return $this->_slash_segment($n, $where, 'rsegment');
  319.     }


  320.      //返回类型为字符串,参数为integer,string,string,string
  321.      //取得一个uri段并且确定是在头还是尾加上一个斜线.
  322.     function _slash_segment($n, $where = 'trailing', $which = 'segment')
  323.     {
  324.         $leading    = '/';
  325.         $trailing    = '/';

  326.         if ($where == 'trailing')
  327.         {
  328.             $leading    = '';
  329.         }
  330.         elseif ($where == 'leading')
  331.         {
  332.             $trailing    = '';
  333.         }

  334.         return $leading.$this->$which($n).$trailing;
  335.     }

  336.     
  337.     //返回类型为数组,路由的段的数组.
  338.     function segment_array()
  339.     {
  340.         return $this->segments;
  341.     }

  342.      //返回类型为数组,重建路由的段的数组.
  343.     function rsegment_array()
  344.     {
  345.         return $this->rsegments;
  346.     }

  347.     //返回类型为字符串,返回段的总数.
  348.     function total_segments()
  349.     {
  350.         return count($this->segments);
  351.     }

  352.     //返回值为整数,即重建路由的段的总数.
  353.     function total_rsegments()
  354.     {
  355.         return count($this->rsegments);
  356.     }

  357.     
  358.      //取得路由的字符串形式,返回类型为字符串.
  359.     function uri_string()
  360.     {
  361.         return $this->uri_string;
  362.     }


  363.     //返回类型为字符串,取得全部的重建路由的字符串.
  364.     function ruri_string()
  365.     {
  366.         return '/'.implode('/', $this->rsegment_array());
  367.     }

  368. }

阅读(1578) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~