Monthly Archives: March 2014

PHP Routing Part 2:Router

上次讲完Request之后,一直在研究Router(路由器)部分的东东。看过PHP、Python等其他框架内实现的路由器。

路由一共分为两个功能:
1、标准路由器, 这里会把链接上的uri根据配置文件分析后,得到Controller和Action。
2、跳转路由和代理路由, 也就是redirect和proxy。同样和1一样,需要写入uri的配置规则,只不过是会根据配置跳转链接,并且赋予301或者302状态码。

在PHP5.4开始的版本,基本上都是使用PHP-FPM模式,当用户访问一个页面,webserver(比如Nginx),会将请求传给php,此时PHP会进行初始化,这时候会将一些变量常量写入到了$_SERVER中。就如上面所说,每次请求都是一个完整的链接,我们分割为3个部分:基本地址,路径,请求参数。

  1. 基本地址: 也就是你的基础路径,可以是一个域名也可以是一个带有path的地址链接。这些将会忽略不计。
  2. 路径: 这里的路径就是你所需的有效路径,比如 /users/show, /video/categories, /news/content/2014-03-02/1313.html
  3. 请求参数: 这里包含了_COOKIE,_GET,_POST以及php://input

一般情况下,我们需要的是路径这一部分,一般可以通过REQUEST_URI获得。这些都是在Request类中的,还没开始进入的Router类中哦,接下来,是路径解析。首先来说说一说标准路由,首先你要对你的期望制定一些路由规则。在路由器中以:开头的,紧跟着的就是变量名称,例如 /:controller/:action 。那么所有在:controller位置的变量都会当作controller名来处理。一般来说我们链接有以下几种需求:
1. 根目录形式 / 需要定义controller和action,指定首页所显示的内容
2. /:controller/:action/* 最常见的链接模型
3. /api/:contoller/:action 链接是有一个prefix的。
4. /news/content/:date/:id.html 一种伪静态的链接。 像这种:date :id是一种自定参数,我们可以在路由配置后面加上 date 的匹配规则, [0-9]{4}-[0-9]{2}-[0-9]{2}。

上面4个例子都是我们常看到的,这些配置有了,还不能正常使用。我们需要通过将这些配置转换成程序可读的正则表达式。首先我们将:controller分割出来,controller将会用于匹配后的key名。这时候正则表达式需要利用标签格式(?exp),这个模式在匹配成功后,返回的结果中,将会以你定的name来做为key,就不需要通过数据索引来一一访问。在匹配成功之后,有了controller和action,这时候Dispatcher就会正式调用。

至此,路由解析已经完成,所有的处理已经全部交给了Controller。Controller处理完结果之后,将会输出给Response,显示给用户的面前。

在Nginx上编程

Nginx是一款高效的http server,它的第三方扩展性也是非常非常之多。这次有个需求,需要通过链接转换成真正的图片地址,将资源rewrite出去。如果资源不存在将随机一张图片返回。

在开始写的过程中,本来想考虑使用LUA模块,但是这么简单的一个功能,觉得利用rewrite功能就可以了,但是他需要随机,nginx自身是不提供随机数的,所以在官方第三方中找到了一个HttpSetMiscModule模块。

原来是通过rewrite功能,将链接根据需求分割获得相应的参数,然后使用set命令,设定nginx下的环境变量。具体看下面的例子。

    location ^~ /live/screenshots/ {
        valid_referers none blocked *.xx.cn; #设定有效来源
        if ($invalid_referer) {
            return 403;
        }
        expires 1m;
        if ($uri ~ /live/screenshots/(\d+)/(\d+)\.(icon|html|htm|jpeg|jpg|gif|bmp|png|flv|swf|zip|rar|doc|docx|xls|xlsx)) { #针对链接进行分析,取出需要的参数
            set $img_path $document_root/live/screenshots/$1/1.$3; #设定nginx环境变量
            set $roomid $1;
            set $gameid $2;
            set $img_type $3;
        }
        if (-e $img_path) { #判断此文件是否存在
            rewrite ^ /live/screenshots/$roomid/1.$img_type break; #存在就重写链接 
        }
        set_random $rand 1 10; #生成随机数
        set $rand_img /live/capture/$gameid/$rand.jpg;
        set $rand_img_path $document_root$rand_img;
        if (-e $rand_img_path) { #判断随机图片
            rewrite ^ $rand_img break;
        }
        error_page 404 =200 http://img.xx.cn/live/NonePic/none.jpg; #如果没有图片,返回404图片,状态仍然保持200
    }

通过上面的例子和注释,应该大致了解了吧。通过rewrite的if提供的regex以-e -f -d函数功能实现了强大的功能。在利用set功能,将nginx的环境进行配置。这个与写一个php或者其他脚本来比较,速度非常快。

我对PHP框架的一些看法

在去年的时候发过一篇对与Request的理解,这个是整个框架中的一小部分。现在php的框架众多,有直接用C实现的yaf;用php语言实现的框架就更多了,例如cakephp,yii,thinkphp,zendframework等等,举不胜举。以前我写过一个小型的PHP框架Suara,现在仍在公司的项目中使用着,但是我一直想把php5.6之前的新特性融合进去,重新对框架进行开发。这时候就遇到第一个难事,如何下手。看过一些stackoverflow的文章,面对PHP框架所需要的功能,有一些疑惑。我们到底需要给予一个框架怎么样的功能?

抛开PHP,我们可以看下Python,其中web.py,就是一个非常好的例子,在众多框架中,是最简单的,只简简单单的提供了router、view、model、controller和wscgi。而其他那些繁杂的功能,全部用使用者或者插件来实现。这个应该最容易上手的一个web框架,只要有一些python语言的基础,并且看一下文档,你就能开发出你所需要的网站。这是我向谈论的第一点:易用性。同样在python中还有flask。在php中我却没有发现,都需要去阅读繁杂的初学者文档,才能一步一步完成。当然,你服务器上只要有php和apache,写个php文件就能成为网站,你可以称它最快速的,我也没什么可以说的了。

第二点,功能性。作为一个框架,所提供的功能的多少,就表现出该框架有多么的强大和方便。虽然这些框架在第一次上手有那么一点苦难,但是在后续开发中,需要一些功能的时候,就显得异常的方便。比如需要一个mail service,一些框架在底层已经完成了实现,而做为网站开发者,只需要对照着框架的文档,写一些配置文件,就能使用。

第三点,性能。众所周知,越强大的框架,所需要占用的内存是直线上升的。这里需要分成两点来说,如果只是用php输出一个”Hello world”。那么直接写php文件的性能肯定是比框架要来的好,快的多,并发量也多。但是如果是查询一个数据然后显示在页面上,那么直接写php上的不一定就有框架来的好了,毕竟框架自身有cache,防dos等等功能。

因此,结合上面的三点,要写一个框架,具体需要看它应用的场景,只有最最符合使用场景的框架才是好框架。

除去router、controller、model和view之外,框架中还有一些其他的常见功能:

  1. Autoload自动加载, 这个是在框架中最为核心的,免去了使用include,require引用。若结合namespace,引用与载入就非常的方便。
  2. Exception异常处理
  3. Log 日志记录
  4. Cache 缓存处理,主要是file、redis、memcache
  5. 邮件

一个框架,需要你很多很多知识,以及丰富的开发经历,才能对框架有一个全局控制。