同福

做个PHP框架(3)路由Router(三)请求资源路径映射处理程序【20201026】

介绍

介绍

童鞋们今天福哥就要带着大家完成路由对象TFRouter的功能了,首先福哥和大家先想一想如何映射才能起到安全又灵活的双重目的。

第一点:既然要安全就是不能直接通过浏览器就可以访问到,这里面就必须借助路由的基本功能将非法请求进行一个屏蔽处理。

第二点:除了安全还需要灵活,灵活就是高度的自由,也就是说我们不能对项目开发人员进行过多的限制,这里面福哥想到的是路径里面的特殊字符是需要屏蔽掉的,其他方面暂时不需要做过多考虑。

TFRouter

我们打开System/TFRouter.inc.php程序文件,建立TFRouter对象。福哥给出了TFRouter对象的主要方法的示例代码,后面会给大家讲解。

a

private function a(string $a):string{
    if($a==""||substr($a,-1)=="/"){
        $a.="index";
    }
    return $a;
}

b

private function b(string $a):string{
    if(preg_match("/\.([^\.]+)\$/",$a,$b)){
        $a=substr($a,0,strlen($a)-strlen($b[0]));
        $_SERVER[$this->b[7]]=strtolower($b[1]);
    }
    if(preg_match("/[\/]+\_([^\/]+)$/",$a,$b)){
        $a=substr($a,0,strlen($a)-strlen($b[0]));
        $_SERVER[$this->b[8]]=$b[1];
    }
    return $a;
}

c

private function c(){
    $a=$_SERVER[$this->b[0]];
    $b=$_SERVER[$this->b[3]];
    $c=TFConfig::get("outRequestUriRoot","system");
    $d=dirname($b);
    $e=strpos($a,"?");
    if($e!==false){
        $f=substr($a,$e+1);
        $f=str_replace("+","%2B",$f);
        $_SERVER[$this->b[1]]=substr($a,0,$e);
        $_GET=array();
        $g=strlen($f);
        $h="";
        $i="";
        $j=0;
        for($k=0;
        $k<$g;
        $k++){
            if($f[$k]=='='){
                $j=1;
                if($h!=""){
                    $_GET[$h]="";
                }
                continue;
            }
            else if($f[$k]=='&'){
                $j=0;
                if($h!=""){
                    $_GET[$h]=urldecode($i);
                    $h="";
                    $i="";
                }
                continue;
            }
            else{
                if($j==0){
                    $h.=$f[$k];
                }
                else if($j==1){
                    $i.=$f[$k];
                }
            }
        }
        if($h!=""){
            $_GET[$h]=urldecode($i);
        }
    }
    else{
        $f="";
        $_SERVER[$this->b[1]]=$a;
        $_GET=array();
    }
    $_SERVER[$this->b[2]]=$f;
    if($d!="/")$d=$d."/";
    if($c!="")$d=$c.$d;
    $_SERVER[$this->b[5]]=$d;
}

d

private function d(string $a,string $b){
    $c=$a."/".$b.FRAMEWORK_CLASS_EXTENSION;
    if(file_exists($c)){
        $this->a=$c;
        return true;
    }
    return false;
}

e

private function e(string $a,string $b,array $c=null){
    $d=strlen($b);
    foreach($c as  $e){
        $f=$e['uri'];
        $g=$e['controller'];
        $h=strlen($f);
        $i=true;
        $j=0;
        $k=0;
        $l=array();
        while($j<$d&&$k<$h){
            if($f[$k]=='{'){
                $m="";
                $k++;
                while($f[$k]!='}'&&$k<$h){
                    $m.=$f[$k];
                    $k++;
                }
                $k++;
                $n="";
                while($b[$j]!='/'&&$j<$d){
                    $n.=$b[$j];
                    $j++;
                }
                $l[$m]=$n;
                continue;
            }
            if($b[$j]!=$f[$k]){
                $i=false;
                break;
            }
            $j++;
            $k++;
        }
        if($j!=$d||$k!=$h){
            $i=false;
        }
        if($i){
            $o=$a."/".$g.FRAMEWORK_CLASS_EXTENSION;
            if(file_exists($o)){
                $this->a=$o;
                $_SERVER[$this->b[6]]=$l;
                return true;
            }
            break;
        }
    }
    return false;
}

f

private function f(string $a,string $b){
    $c=$a."/".$b.FRAMEWORK_CLASS_EXTENSION;
    $d=dirname($c);
    while(true){
        $c=$d."/.mapping".FRAMEWORK_CLASS_EXTENSION;
        if(file_exists($c)){
            $this->a=$c;
            return true;
            break;
        }
        else if($d==$a){
            break;
        }
        $d=dirname($d);
    }
    return false;
}

g

private function g(string $a,string $b,array $c=null){
    $d=$a."Controller";
    $e=false;
    if(!$e&&$this->d($d,$b)){
        $e=true;
    }
    if(!$e&&$this->e($d,$b,$c)){
        $e=true;
    }
    if(!$e&&$this->f($d,$b)){
        $e=true;
    }
    if($e){
        $_SERVER[$this->b[4]]=$this->a;
        $_SERVER[$this->b[3]]=substr($this->a,strlen($d));
        return $this->a;
    }
    else{
        $_SERVER[$this->b[3]]=$_SERVER[$this->b[4]]="";
        return null;
    }
}

h

private function h(string $a,string $b,array $c=null){
    $d=new TFPHP();
    $b=$this->a($b);
    $b=$this->b($b);
    $this->c();
    $e=$this->g($a,$b,$c);
    if($e!=null){
        include_once($e);
        if(class_exists('PageController')){
            $f=new\PageController($d);
            $f->load($e);
        }
        else if(class_exists('APIController')){
            $f=new\APIController($d);
            $f->load();
        }
        else{
            return $d->getResponse()->responseStatus(500);
        }
    }
    else{
        return $d->getResponse()->responseStatus(404);
    }
}

TFRouteMap

我们打开TFRouteMap.php程序文件,在里面调用TFRouter对象,实现路由功能。

use TFPHP\System\TFRouter;

include_once ('./System/Autoload.inc.php');

new TFRouter(
    __DIR__. "/WEB-INF/",
    $_SERVER['QUERY_STRING']
);

讲解

TFRouter

福哥来讲解一下TFRouter路由对象的逻辑,首先福哥没有给出构造器的代码,在构造器里面会调用map方法进行资源路径的映射操作。

a

该方法判断如果用户请求的资源路径是一个目录而不是一个具体的文件名的话,我们就默认寻找“index”这个文件名。

b

该方法用来解析请求的资源路径包括的扩展名等等信息,这里只是提取了扩展名,后面大家可以解析资源路径的更多信息出来。

c

该方法通过REUQST_URI环境参数,拆解出真实的QUERY_STRING、PHP_URI、SCRIPT_NAME等等参数。

d

检查映射程序文件是否存在

e

解析通过配置方式设置的URI路径

f

解析自动寻找方式的URI路径

g

这个方法是资源路径映射的核心方法,首先通过直接(direct)方法进行映射探测,如果直接映射成功就返回了。否则就进行手动(manual)方法进行映射探测,而且是一级一级地向上翻,直到找到可以成功实现手动方法映射的文件(.mapping.inc.php)为止。

h

这个方法是入口方法,用来将资源路径映射操作串起来,实现路由的基本功能。

TFRouteMap

这个TFRouteMap.php是我们通过rewrite重写将所有访问都引入到这个文件里面的,这个文件会引用System/TFRouter对象,然后初始化TFRouter对象,并传入“WEB-INF”绝对路径和环境变量QUERY_STRING(也就是rewrite引入的原请求资源路径),这样TFRouter就会对原请求资源路径按照我们设想的那样进行映射处理了。

测试

下面我们来测试一下路由模块的工作是否正常,分为两个模式:direct(直接映射)、manual(手动映射)。

http://192.168.1.168:8068/

90806263b2d7b2c6.jpg

http://192.168.1.168:8068/aboutus

c9fd6d1917cb5d6f.jpg

http://192.168.1.168:8068/products/tfapp

fb2ac38a8e1e9fd1.jpg

http://192.168.1.168:8068/download/tfapp/v1.0.0/tfapp-v1.0.0.zip

baa9761e21689eab.jpg

代码混淆

福哥给出的代码都是经过混淆处理的,这是对TFPHP的一种保护措施。福哥不希望TFPHP会被有心人拿去二次开发便成为了他人的“原创”作品了。

这个TFPHP毕竟是福哥花费心思设计,一个字符一个字符地敲出来的,福哥后面会把完整的源代码发布出来给童鞋们下载,不过代码会做混淆处理。

总结

福哥今天带着大家实现了路由模块TFRouter对象的基本功能,可以对任意访问请求的资源路径进行自动的映射了。但是,目前这个路由模块还是很简单的,没有考虑太多的因素,也没有提取足够多的环境信息,真正投入生产环境的时候,这样的路由模块是无法满足需要的,那么福哥会在后面将完善后的路由模块随着正式版本TFPHP一起发布出来的,敬请期待吧~~

下一课福哥会带着童鞋们开始了解控制器模块TFController的设计了,控制器模块涉及到的内容比较多,大家要跟上福哥哦~~

P.S.

微信公众号的文章发出去之后是不能编辑的,但是福哥偶尔会修复一些描述不到位、示例不正确、结构不清晰等等的文章错误,这些只能在网站上才能看到最新版本内容,望大家知晓~~