30 07 2020

public/index.php

<?php

......

// 执行应用并响应
Container::get('app')->run()->send();

Container:: get('app')最终将指向了 think\App.php类,run()内部首先调用了initialize();


thinkphp/library/think/App.php

<?php

......

/**
 * 执行应用程序
 * @access public
 * @return Response
 * @throws Exception
 */
public function run()
{
    try {
        // 初始化应用
        $this->initialize();
        
        ......
    }   
    
    ...... 
    
}


/**
 * 初始化应用
 * @access public
 * @return void
 */
public function initialize()
{
    ......
    
    // 路由初始化
    $this->routeInit();
}


routeInit()会读取route目录下的所有路由文件判断路由文件的返回值,从而进行不同的处理


thinkphp/library/think/App.php

/**
 * 路由初始化 导入路由定义规则
 * @access public
 * @return void
 */
public function routeInit()
{
    // 路由检测
    $files = scandir($this->routePath);
    foreach ($files as $file) {
        if (strpos($file, '.php')) {
            $filename = $this->routePath . $file;
            // 导入路由配置
            $rules = include $filename;
            if (is_array($rules)) {
                $this->route->import($rules);
            }
        }
    }

    if ($this->route->config('route_annotation')) {
        // 自动生成路由定义
        if ($this->appDebug) {
            $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
            $this->build->buildRoute($suffix);
        }

        $filename = $this->runtimePath . 'build_route.php';

        if (is_file($filename)) {
            include $filename;
        }
    }
}


路由文件,通过门面模式找到了facade/route类,并调用其get方法

route/route.php

<?php

Route::get('think', function () {
    return 'hello,ThinkPHP5!';
});

Route::get('hello/:name', 'index/hello');

return [

];


官方手册示例如下

Route::get('new/:id','News/read'); // 定义GET请求路由规则
Route::post('new/:id','News/update'); // 定义POST请求路由规则
Route::put('new/:id','News/update'); // 定义PUT请求路由规则
Route::delete('new/:id','News/delete'); // 定义DELETE请求路由规则
Route::any('new/:id','News/read'); // 所有请求都支持的路由规则

如果要定义get和post请求支持的路由规则,也可以用

Route::get('new/:id','News/read','GET|POST');


无论是get() 还是 post() 还是 rule(),其本质都是调用 addRule()


thinkphp/library/think/Route.php

<?php

class Route
{
    ......
    
    /**
     * 注册路由规则
     * @access public
     * @param  string    $rule       路由规则
     * @param  mixed     $route      路由地址
     * @param  string    $method     请求类型
     * @param  array     $option     路由参数
     * @param  array     $pattern    变量规则
     * @return RuleItem
     */
    public function rule($rule, $route, $method = '*', array $option = [], array $pattern = [])
    {
        return $this->group->addRule($rule, $route, $method, $option, $pattern);
    }
    
    ......
}


thinkphp/library/think/route/RuleGroup.php

<?php

class RuleGroup extends Rule
{
    
    /**
     * 添加分组下的路由规则或者子分组
     * @access public
     * @param  string    $rule       路由规则
     * @param  string    $route      路由地址
     * @param  string    $method     请求类型
     * @param  array     $option     路由参数
     * @param  array     $pattern    变量规则
     * @return $this
     */
    public function addRule($rule, $route, $method = '*', $option = [], $pattern = [])
    {
        // 读取路由标识
        if (is_array($rule)) {
            $name = $rule[0];
            $rule = $rule[1];
        } elseif (is_string($route)) {
            $name = $route;
        } else {
            $name = null;
        }
    
        $method = strtolower($method);
    
        if ('/' === $rule || '' === $rule) {
            // 首页自动完整匹配
            $rule .= '$';
        }
    
        // 创建路由规则实例
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method, $option, $pattern);
    
        if (!empty($option['cross_domain'])) {
            $this->router->setCrossDomainRule($ruleItem, $method);
        }
    
        $this->addRuleItem($ruleItem, $method);
    
        return $ruleItem;
    }

}


new RuleItem 会创建路由规则实例

thinkphp/library/think/route/RuleItem.php

<?php

class RuleItem extends Rule
{

    ......
    
    /**
     * 架构函数
     * @access public
     * @param  Route             $router 路由实例
     * @param  RuleGroup         $parent 上级对象
     * @param  string            $name 路由标识
     * @param  string|array      $rule 路由规则
     * @param  string|\Closure   $route 路由地址
     * @param  string            $method 请求类型
     * @param  array             $option 路由参数
     * @param  array             $pattern 变量规则
     */
    public function __construct(Route $router, RuleGroup $parent, $name, $rule, $route, $method = '*', $option = [], $pattern = [])
    {
        $this->router  = $router;
        $this->parent  = $parent;
        $this->name    = $name;
        $this->route   = $route;
        $this->method  = $method;
        $this->option  = $option;
        $this->pattern = $pattern;
    
        $this->setRule($rule);
    
        if (!empty($option['cross_domain'])) {
            $this->router->setCrossDomainRule($this, $method);
        }
    }

    /**
     * 路由规则预处理
     * @access public
     * @param  string      $rule     路由规则
     * @return void
     */
    public function setRule($rule)
    {
        if ('$' == substr($rule, -1, 1)) {
            // 是否完整匹配
            $rule = substr($rule, 0, -1);
    
            $this->option['complete_match'] = true;
        }
    
        $rule = '/' != $rule ? ltrim($rule, '/') : '';
    
        if ($this->parent && $prefix = $this->parent->getFullName()) {
            $rule = $prefix . ($rule ? '/' . ltrim($rule, '/') : '');
        }
    
        if (false !== strpos($rule, ':')) {
            $this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $rule);
        } else {
            $this->rule = $rule;
        }
    
        // 生成路由标识的快捷访问
        $this->setRuleName();
    }
    
    ......
}