关于 REST 的介绍可以参考我之前的文章,总体说来,REST 是 web 发展的趋势,而 PHP 是 web 开发的利器,但我找了一遍,只找到了两个 PHP REST 框架(不包括那些以 MVC 为核心,同时又支持 REST 的框架),一个是Tonic,架构理念我比较认同,但代码质量实在不敢恭维。还有一个是Recess,在我看来,它有点复杂化了,把不该 rest 做的事也做了。在这种情况下,我只能自己动手,丰衣足食了。
RESTY 简介
RESTY 的流程很简单,获取 Request 单例,然后执行 exec 方法,该方法里会调用 Route 来解析 URI 获取相应的 Resource,然后实例化 Resource,触发相应的 HTTP 方法,最后返回一个 Response 对象,Response 执行 output 方法就输出了结果。听起来好像一点都不简单,哈哈,还是来大概看一下代码吧
index.php
<?php
try {
Request::instance()->exec()->output();
} catch (Route_Exception $e) {
Response::instance()
->set_status(404)
->set_body(array(
'error' => 'Resource Not Found',
'Request' => $_SERVER['REQUEST_URI'],
))
->output()
;
}
request.php
<?php
public function exec()
{
$class_name = 'Resource_'.str_replace('/', '_', $this->get_resource());
$class = new ReflectionClass($class_name);
$resource = $class->newInstance($this);
$class->getMethod('before')->invoke($resource);
$class->getMethod($this->request_method)->invoke($resource);
$class->getMethod('after')->invoke($resource);
$response = Response::instance();
$response->set_body($resource->get_data());
return $response;
}
response.php
<?php
public function output()
{
$this->_content_encoding();
header('Content-type:application/json;charset=utf-8');
header('Status:'.$this->_status.' '.$this->_messages[$this->_status]);
header('Content-Length: '.strlen($this->_body));
foreach($this->_header as $key => $val)
{
header($key.':'.$val);
}
echo $this->_body;
}
RESTY 特性
轻量级
RESTY 包含了核心的 Request/Resource/Response/Route/Config/Validation 功能,没有其他多余的部件,如 Controller/View 等等,很纯粹。一个工具应该把一件事做好,同时提供接口,这也是 RESTY 的哲学。
使用方便
使用时,只需定义好 uri 对应的 Resource,然后编写 Resource 就行了,其他的事 RESTY 会帮你搞定。
config/resource.php demo
<?php
return array(
'/example/(?<id>[0-9]+)' => 'example',
'/example/foo/(?<name>[a-zA-Z_0-9]+)' => 'example/foo',
);
可以看到 uri 支持正则,没错,原生的 php 正则。resource 部分对应 resource 文件的路径(不包括后缀)
resource/example.php
<?php
class Resource_Example extends Resource
{
public function get()
{
/* set etag
Response::instance()
->if_none_match(md5('hello'))
->add_etag(md5('hello'))
;
//*/
if ($this->validate())
{
$this->_data = $this->get_data();
}
else
{
$this->_data = array('error' => implode(',', $this->getErrors()), 'request' => $_SERVER['REQUEST_URI']);
}
}
public function post()
{
$this->_data = array_merge($this->get_data(), array('type' => 'post'));
}
}
每一个资源对应 4 个 http 方法。RESTY 还很贴心地提供了 Validation 部件(基本上是直接从 Kohana 中 K 过来的),方便对数据进行校验。
易扩展
system/classes 文件夹下的类文件,都可以在 app/classes 文件夹下扩展,而且使用时不用做任何修改。假设你之前已经写了不少 Resource,忽然想到要扩展系统的 Resource 类,正常的做法是定义一个 MY_Resource 之类的类文件来扩展系统的 Resource 类,然后使用时使用 MY_Resource 而不是 Resource。但这样就会有个问题,之前使用的 Resource 类都要做修改了,可谓牵一发而动全身。RESTY 就方便了,同样要扩展 Resource 类,只要在 app/classes 下新建一个 resource.php 文件,然后扩展 Resty_Resource 类即可。
<?php
class Resource extends Resty_Resource
{
public function foo()
{
//...
}
}
这样使用时还是一样的 Resource 类,但却多了 foo 方法。这也是从 Kohana 学到的无缝扩展大法(题外话:Kohana 真是个不错的框架,各位不妨一试)。原理就是在类自动加载时会先去 app/classes 文件夹下去找,如果没找到的话再去 system/classes 下找。
验证功能
作为一个比较完整的 REST 框架,Validation 还是不能少的,为了不重复制造轮子,直接把 Kohana 的验证类搬了过来,稍作修改。
配置:config/validation.php
<?php
return array(
'example' => array(
'get' => array(
'filters' => array(
'id' => array(
'trim' => null,
),
),
'rules' => array(
'id' => array(
'not_empty' => null,
'min_length' => array(2),
'digit' => null,
),
),
),
),
);
错误提示:config/message.php
<?php
return array(
'example' => array(
'id' => array(
'digit' => 'id必须是数字',
'not_empty' => 'id不能为空',
'min_length' => 'id长度至少为:value',
),
),
);
Config 功能
config 文件如上面所示,就是返回一个数组。使用也很简单:
// 获取config/message.php文件的example key对应的内容
Config::get('message.example');
// 设置config(不会写入到文件,只在一个http request有效)
Config::set('message.example.id.digit', 'id can be anything');
下载
https://github.com/limboy/resty欢迎使用,并反馈:)