¶«É­Æ½Ì¨ADLab£ºThinkPHP5Ô¶³Ì´úÂëÖ´ÐЩ¶´·ÖÎö

Ðû²¼Ê±¼ä 2018-12-11
©¶´½éÉÜ

2018Äê12ÔÂ9ÈÕ £¬ThinkPHPÍŶÓÐû²¼ÁËÒ»¸ö²¹¶¡¸üР£¬ÐÞ¸´ÁËÒ»´¦ÓÉÓÚ·ÓɽâÎöȱÏݵ¼ÖµĴúÂëÖ´ÐЩ¶´¡£¸Ã©¶´Î£º¦Ë®Æ½·Ç³£¸ß £¬Ä¬ÈÏ»·¾³ÅäÖü´¿Éµ¼ÖÂÔ¶³Ì´úÂëÖ´ÐС£¾­¹ý¶«É­Æ½Ì¨ADLabÄþ¾²Ñо¿Ô±¶ÔThinkPHPµÄ56¸öС°æ±¾µÄÔ´Âë·ÖÎöºÍÑéÖ¤ £¬È·¶¨¾ßÌåÊÜÓ°ÏìµÄ°æ±¾Îª:


ThinkPHP 5.0.5-5.0.22

ThinkPHP 5.1.0-5.1.30


©¶´¸´ÏÖ


µ±µØ»·¾³½ÓÄÉThinkPHP 5.1.29+PHP7+Apache½øÐи´ÏÖ¡£°²×°»·¾³ºóÖ±½Ó·ÃÎÊPOC¸ø¶¨µÄURL¼´¿ÉÖ´ÐÐphpinfo() £¬ÈçͼËùʾ¡£

¶«É­¡¤(ÖйúÇø)¹Ù·½ÍøÕ¾


©¶´·ÖÎö


ÒÔ5.1.29°æ±¾½øÐзÖÎö £¬Ê×ÏÈ¿´È¡Â·Óɵĺ¯Êýpathinfo£º

library/think/Request.php:678

public function pathinfo()
    {
        if (is_null($this->pathinfo)) {
            if (isset($_GET[$this->config['var_pathinfo']])) {
                // ÅжÏURLÀïÃæÊÇ·ñÓмæÈÝģʽ²ÎÊý
                $pathinfo = $_GET[$this->config['var_pathinfo']];
                unset($_GET[$this->config['var_pathinfo']]);
            } elseif ($this->isCli()) {
                // CLIģʽÏ index.php module/controller/action/params/...
                $pathinfo = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
            } elseif ('cli-server' == PHP_SAPI) {
                $pathinfo = strpos($this->server('REQUEST_URI'), '?') ? strstr($this->server('REQUEST_URI'), '?', true) : $this->server('REQUEST_URI');
            } elseif ($this->server('PATH_INFO')) {
                $pathinfo = $this->server('PATH_INFO');
            }
 
            // ·ÖÎöPATHINFOÐÅÏ¢
            if (!isset($pathinfo)) {
                foreach ($this->config['pathinfo_fetch'] as $type) {
                    if ($this->server($type)) {
                        $pathinfo = (0 === strpos($this->server($type), $this->server('SCRIPT_NAME'))) ?
                        substr($this->server($type), strlen($this->server('SCRIPT_NAME'))) : $this->server($type);
                        break;
                    }
                }
            }
 
            $this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/');
        }
 
        return $this->pathinfo;
    }


¸Ã·Óɺ¯ÊýÖÐ$this->config['var_pathinfo']ÊÇÅäÖÃÎļþµÄĬÈÏÖµ £¬Æä³õʼ»¯´úÂëÈçÏ £¬ÖµÎª¡¯s¡¯:


¶«É­¡¤(ÖйúÇø)¹Ù·½ÍøÕ¾



µ±ÇëÇó±¨ÎÄ°üÂÞ$_GET['s'] £¬¾ÍÈ¡ÆäÖµ×÷Ϊpathinfo £¬²¢·µ»Øpathinfo¸øµ÷Óú¯Êý¡£

·ÖÎö·¢ÏÖpathinfoº¯Êý±»library/think/Request.php:716ÖеÄpathº¯Êýµ÷Óãº

public function path()
{
    if (is_null($this->path)) {
        $suffix   = $this->config['url_html_suffix'];
        $pathinfo = $this->pathinfo();


        if (false === $suffix) {
            // ½ûֹα¾²Ì¬·ÃÎÊ
            $this->path = $pathinfo;
        } elseif ($suffix) {
            // È¥³ýÕý³£µÄURLºó׺
            $this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
        } else {
            // ÔÊÐíÈκκó׺·ÃÎÊ
            $this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo);
        }
    }
    return $this->path;
}

 

ÏÔÈ» £¬ÕâÀï$this->pathÔ´×Ôpathinfo £¬Òò´Ë¿ÉÒÔ±»¹¥»÷Õß¿ØÖÆ¡£¼ÌÐø·ÖÎö¸Ã±äÁ¿µÄͨ±¨ £¬ÔÚlibrary/think/App.php:597Öб»ÒýÓãº

//public function routecheck()
$path = $this->request->path();
 
        // ÊÇ·ñÇ¿ÖÆ·ÓÉģʽ
        $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
 
        // ·Óɼì²â ·µ»ØÒ»¸öDispatch¹¤¾ß
        $dispatch = $this->route->check($path, $must);
 
        if (!empty($routeKey)) {
            try {
                if ($option) {
                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
                } else {
                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);
                }
            } catch (\Exception $e) {
                // ´æÔÚ±Õ°üµÄʱºò»º´æÎÞЧ
            }
        }
 

        return $dispatch;


ÕâÀïÊǽøÐзÓɼì²â £¬¹¥»÷Õ߿ɿصÄ$path±»Í¨±¨¸øÁËÈçϵÄcheckº¯Êý£º

public function check($url, $must = false)
    {
        // ×Ô¶¯¼ì²âÓòÃû·ÓÉ
        $domain = $this->checkDomain();
        $url    = str_replace($this->config['pathinfo_depr'], '|', $url);
 
        $completeMatch = $this->config['route_complete_match'];
 
        $result = $domain->check($this->request, $url, $completeMatch);
 
        if (false === $result && !empty($this->cross)) {
            // ¼ì²â¿çÓò·ÓÉ
            $result = $this->cross->check($this->request, $url, $completeMatch);
        }
 
        if (false !== $result) {
            // ·ÓÉÆ¥Åä
            return $result;
        } elseif ($must) {
            // Ç¿ÖÆ·Óɲ»Æ¥ÅäÔòÅ׳öÒì³£
            throw new RouteNotFoundException();
        }
        // ĬÈÏ·ÓɽâÎö
        return new UrlDispatch($this->request, $this->group, $url, [
            'auto_search' => $this->autoSearchController,
        ]);
    }


·ÖÎö´úÂë¿ÉÖª £¬Èç¹û¿ªÆôÁËÇ¿ÖÆ·ÓÉÔò»áÅ׳öÒì³£ £¬Ò²¾ÍÊǹٷ½Ëù˵µÄ¸Ã©¶´ÔÚ¿ªÆôÇ¿ÖÆ·ÓɵÄÇé¿öϲ»ÊÜÓ°Ï죨ĬÈϲ»¿ªÆô£©¡£


Checkº¯Êý×îºóʵÀý»¯Ò»¸öUrlDispatch¹¤¾ß £¬½«$urlͨ±¨¸øÁ˽ṹº¯Êý¡£¼ÌÐø·ÖÎöUrlDispatchµÄ¸¸ÀàÒ²¾ÍÊÇDispatchÀàµÄ½á¹¹º¯Êý:

library/think/route/Dispatch.php:64


 public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null)
    {
        $this->request  = $request;
        $this->rule     = $rule;
        $this->app      = Container::get('app');
        $this->dispatch = $dispatch;
        $this->param    = $param;
        $this->code     = $code;
 
        if (isset($param['convert'])) {
            $this->convert = $param['convert'];
        }
    }


$dispatch±äÁ¿¿É¿Ø²¢¸³Öµ¸øÁË$this->dispatch £¬¾­¹ý¶à´Îº¯Êýµ÷Ó÷µ»Ø £¬×îºóÈçϵÄUrlÀàµÄinit º¯Êý½«»á±»µ÷ÓÃÀ´´¦ÖÃ$this->dispatch¡£

class Url extends Dispatch
{
    public function init()
    {
        // ½âÎöĬÈϵÄURL¹æÔò
        $result = $this->parseUrl($this->dispatch);
 
        return (new Module($this->request, $this->rule, $result))->init();
    }
 
    public function exec()
    {}


ÕâÀïµ÷ÓÃparseUrl¶Ô$this->dispatch½øÐнâÎö £¬ÕâÊǸ鶴µÄºËÐĵãÖ®Ò»£º

protected function parseUrl($url)
{
    $depr = $this->rule->getConfig('pathinfo_depr');
    $bind = $this->rule->getRouter()->getBind();
 
    if (!empty($bind) && preg_match('/^[a-z]/is', $bind)) {
        $bind = str_replace('/', $depr, $bind);
        // Èç¹ûÓÐÄ£¿é/¿ØÖÆÆ÷°ó¶¨
        $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
    }
 
    list($path, $var) = $this->rule->parseUrlPath($url);
    if (empty($path)) {


ÕâÀïµ÷ÓÃparseUrlPathº¯Êý¶Ô$url½øÐнâÎö £¬¼ÌÐø·ÖÎö¸Ãº¯Êý£º


public function parseUrlPath($url)

    {
       ....
    ....
        } elseif (strpos($url, '/')) {
            // [Ä£¿é/¿ØÖÆÆ÷/²Ù×÷]
            $path = explode('/', $url);
        } elseif (false !== strpos($url, '=')) {
            // ²ÎÊý1=Öµ1&²ÎÊý2=Öµ2...
            $path = [];
            parse_str($url, $var);
        } else {
            $path = [$url];
        }
 
        return [$path, $var];
    }


ÏÔÈ» £¬$urlµÄ¸ñʽΪ¡°Ä£¿é/¿ØÖÆÆ÷/²Ù×÷¡± £¬$urlÖ§½âÐγÉÒ»¸öÊý×é´æµ½$path±äÁ¿Öв¢·µ»Øµ½µ÷ÓÃÕß¡£


¼ÌÐø·ÖÎö·âװ·ÓɵĴúÂë:


library/think/route/dispatch/Url.php:48

 list($path, $var) = $this->rule->parseUrlPath($url);
    ...
    ...
        // ½âÎöÄ£¿é
        $module = $this->rule->getConfig('app_multi_module') ? array_shift($path) : null;
 
        if ($this->param['auto_search']) {
            $controller = $this->autoFindController($module, $path);
        } else {
            // ½âÎö¿ØÖÆÆ÷
            $controller = !empty($path) ? array_shift($path) : null;
        }
 
        // ½âÎö²Ù×÷
        $action = !empty($path) ? array_shift($path) : null;
        ...
        ...
        // ÉèÖõ±Ç°ÇëÇóµÄ²ÎÊý
        $this->request->setRouteVars($var);
 
        // ·âװ·ÓÉ
        $route = [$module, $controller, $action];
        return $route;


·ÓÉ·â×°·µ»Øµ½library/think/route/dispatch/Url.php:20


class Url extends Dispatch

{
    public function init()
    {
        // ½âÎöĬÈϵÄURL¹æÔò
        $result = $this->parseUrl($this->dispatch);
 
        return (new Module($this->request, $this->rule, $result))->init();
    }


$result¾ÍÊÇ·â×°ºÃµÄ·ÓÉÊý×é £¬Í¨±¨¸øÁËModuleµÄ½á¹¹º¯Êý¡£
ÓÉÓÚModuleÒ²ÊǼ̳Ð×ÔDispatchÀà £¬Ö±½Ó¿´DispatchµÄ½á¹¹º¯Êý:


public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null)
    {
        $this->request  = $request;
        $this->rule     = $rule;
        $this->app      = Container::get('app');
        $this->dispatch = $dispatch;
        $this->param    = $param;
        $this->code     = $code;
 
        if (isset($param['convert'])) {
            $this->convert = $param['convert'];
        }
    }


$result¸³Öµ¸øÁË$this->dispatch¡£È»ºóµ÷ÓÃModuleÀàµÄinitº¯Êý£º


public function init()
    {
        parent::init();
        $result = $this->dispatch;
  
        if ($this->rule->getConfig('app_multi_module')) {
            // ¶àÄ£¿é²¿Êð
            $module    = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module')));
            ...
            ...
            } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) {
                $available = true;
            } 
           ...
           ...
            // Ä£¿é³õʼ»¯
            if ($module && $available) {
                // ³õʼ»¯Ä£¿é
                $this->request->setModule($module);
                $this->app->init($module);
            } else {
                throw new HttpException(404, 'module not exists:' . $module);
            }
        }
        // »ñÈ¡¿ØÖÆÆ÷Ãû
        $controller       = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));
        $this->controller = $convert ? strtolower($controller) : $controller;
        // »ñÈ¡²Ù×÷Ãû
        $this->actionName = strip_tags($result[2] ?: $this->rule->getConfig('default_action'));
        // ÉèÖõ±Ç°ÇëÇóµÄ¿ØÖÆÆ÷¡¢²Ù×÷
        $this->request
            ->setController(Loader::parseName($this->controller, 1))
            ->setAction($this->actionName);
 
        return $this;
    }


ÕâÀï´æÔÚµÚÒ»¸ö¶Ô$moduleµÄÅжÏ £¬ÐèÒªÈÃ$available¼´ÊÇtrue £¬Õâ¾ÍÐèÒªis_dir($this->app->getAppPath() . $module)½¨Á¢¡£¹Ù·½demo¸ø³öµÄÄ£¿éÊÇindex £¬¶øʵ¼Ê¿ª·¢·¨Ê½·×Æ綨´æÔÚ¸ÃÄ£¿éÃû £¬ËùÒԽṹpayloadʱÕâÀïÊÇÒ»¸ö×¢Òâµã¡£


Âú×ãÕâ¸öÅжÏÌõ¼þºó £¬¼ÌÐø·ÖÎöºóÐøµÄ¿ØÖÆÁ÷»á½øÈëÈçÏÂmoduleµÄexecº¯Êý£ºlibrary/think/route/dispatch/Module.php:80


public function exec()

    {
        // ¼àÌýmodule_init
        $this->app['hook']->listen('module_init');
 
        try {
            // ʵÀý»¯¿ØÖÆÆ÷
            $instance = $this->app->controller($this->controller,
                $this->rule->getConfig('url_controller_layer'),
                $this->rule->getConfig('controller_suffix'),
                $this->rule->getConfig('empty_controller'));
 
            if ($instance instanceof Controller) {
                $instance->registerMiddleware();
            }
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }


·ÖÎö·¢ÏÖ £¬$this->controllerÊǹ¥»÷Õ߿ɿصÄ £¬²¢Í¨±¨¸øÁËÈçϵÄcontrollerº¯Êý £¬¼ÌÐø·ÖÎö¸Ãº¯Êý£º


  public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
    {
        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
 
        if (class_exists($class)) {
            return $this->__get($class);
        } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
            return $this->__get($emptyClass);
        }
        throw new ClassNotFoundException('class not exists:' . $class, $class);
    }


ÔÚÕâÀï £¬nameÊǹ¥»÷Õ߿ɿصÄ £¬²¢Í¨±¨¸øÁËÈçϵÄparseModuleAndClassº¯Êý£º


protected function parseModuleAndClass($name, $layer, $appendSuffix)

    {
        if (false !== strpos($name, '\\')) {
            $class  = $name;
            $module = $this->request->module();
        } else {
            if (strpos($name, '/')) {
                list($module, $name) = explode('/', $name, 2);
            } else {
                $module = $this->request->module();
            }
 
            $class = $this->parseClass($module, $layer, $name, $appendSuffix);
        }
 
        return [$module, $class];
    }


·ÖÎö·¢ÏÖ £¬µ±$name´æÔÚ·´Ð±¸Üʱ¾ÍÖ±½Ó½«$name¸³Öµ¸ø$class²¢·µ»Ø¡£ÏÔÈ» £¬¹¥»÷Õßͨ¹ý¿ØÖÆÊäÈë¾Í¿ÉÒÔ²Ù¿ØÀàµÄʵÀý»¯¹ý³Ì £¬´Ó¶øÔì³É´úÂëÖ´ÐЩ¶´¡£



²¹¶¡·ÖÎö

ÔÚThinkPHP5.0.23ÒÔ¼°5.1.31°æ±¾ÖÐ £¬Ôö¼ÓÁ˶Ô$controllerµÄÕýÔò¹ýÂË£º


µ¼ÖÂÎÞ·¨ÔÙ´«Èë\think\appÕâÖÖÐÎʽµÄ¿ØÖÆÆ÷¡£


½á ÂÛ

´Ë©¶´ÊÇÒòΪ¿ò¼Ü¶Ô´«ÈëµÄ·ÓɲÎÊý¹ýÂ˲»Ñϸñ £¬µ¼Ö¹¥»÷Õß¿ÉÒÔ²Ù×÷·ÇÔ¤ÆڵĿØÖÆÆ÷ÀàÀ´Ô¶³ÌÖ´ÐдúÂë¡£½øÒ»²½·ÖÎö·¢ÏÖ £¬Ä³Ð©ThinkPHP°æ±¾²»ÊÜÒѹûÈ»µÄPOCµÄÓ°Ïì £¬ÕâÊÇÓÉÓÚ¸ÃPOCȱ·¦Í걸ÐÔ¿¼ÂÇ¡£Òò´Ë £¬Ç¿ÁÒ½¨ÒéÓû§¼°Ê±½«5.0.x°æ±¾Éý¼¶µ½5.0.23 £¬½«5.1.x°æ±¾Éý¼¶µ½5.1.31 £¬ÒÔÃâÔâÊܹ¥»÷¡£