写了一款REST框架——RESTY

关于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/lzyy/resty

欢迎使用,并反馈:)


--EOF--

若无特别说明,本站文章均为原创,转载请保留链接,谢谢