RestAction.class.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. defined('THINK_PATH') or exit();
  12. /**
  13. * ThinkPHP Restful 控制器扩展
  14. * @category Extend
  15. * @package Extend
  16. * @subpackage Action
  17. * @author liu21st <liu21st@gmail.com>
  18. */
  19. abstract class RestAction {
  20. // 当前Action名称
  21. private $name = '';
  22. // 视图实例
  23. protected $view = null;
  24. protected $_method = ''; // 当前请求类型
  25. protected $_type = ''; // 当前资源类型
  26. // 输出类型
  27. protected $_types = array();
  28. /**
  29. * 架构函数 取得模板对象实例
  30. * @access public
  31. */
  32. public function __construct() {
  33. //实例化视图类
  34. $this->view = Think::instance('View');
  35. if(!defined('__EXT__')) define('__EXT__','');
  36. // 资源类型检测
  37. if(''==__EXT__) { // 自动检测资源类型
  38. $this->_type = $this->getAcceptType();
  39. }elseif(false === stripos(C('REST_CONTENT_TYPE_LIST'),__EXT__)) {
  40. // 资源类型非法 则用默认资源类型访问
  41. $this->_type = C('REST_DEFAULT_TYPE');
  42. }else{
  43. // 检测实际资源类型
  44. if($this->getAcceptType() == __EXT__) {
  45. $this->_type = __EXT__;
  46. }else{
  47. $this->_type = C('REST_DEFAULT_TYPE');
  48. }
  49. }
  50. // 请求方式检测
  51. $method = strtolower($_SERVER['REQUEST_METHOD']);
  52. if(false === stripos(C('REST_METHOD_LIST'),$method)) {
  53. // 请求方式非法 则用默认请求方法
  54. $method = C('REST_DEFAULT_METHOD');
  55. }
  56. $this->_method = $method;
  57. // 允许输出的资源类型
  58. $this->_types = C('REST_OUTPUT_TYPE');
  59. //控制器初始化
  60. if(method_exists($this,'_initialize'))
  61. $this->_initialize();
  62. }
  63. /**
  64. * 获取当前Action名称
  65. * @access protected
  66. */
  67. protected function getActionName() {
  68. if(empty($this->name)) {
  69. // 获取Action名称
  70. $this->name = substr(get_class($this),0,-6);
  71. }
  72. return $this->name;
  73. }
  74. /**
  75. * 是否AJAX请求
  76. * @access protected
  77. * @return boolean
  78. */
  79. protected function isAjax() {
  80. if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) ) {
  81. if('xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH']))
  82. return true;
  83. }
  84. if(!empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')]))
  85. // 判断Ajax方式提交
  86. return true;
  87. return false;
  88. }
  89. /**
  90. * 魔术方法 有不存在的操作的时候执行
  91. * @access public
  92. * @param string $method 方法名
  93. * @param array $args 参数
  94. * @return mixed
  95. */
  96. public function __call($method,$args) {
  97. if( 0 === strcasecmp($method,ACTION_NAME)) {
  98. if(method_exists($this,$method.'_'.$this->_method.'_'.$this->_type)) { // RESTFul方法支持
  99. $fun = $method.'_'.$this->_method.'_'.$this->_type;
  100. $this->$fun();
  101. }elseif($this->_method == C('REST_DEFAULT_METHOD') && method_exists($this,$method.'_'.$this->_type) ){
  102. $fun = $method.'_'.$this->_type;
  103. $this->$fun();
  104. }elseif($this->_type == C('REST_DEFAULT_TYPE') && method_exists($this,$method.'_'.$this->_method) ){
  105. $fun = $method.'_'.$this->_method;
  106. $this->$fun();
  107. }elseif(method_exists($this,'_empty')) {
  108. // 如果定义了_empty操作 则调用
  109. $this->_empty($method,$args);
  110. }elseif(file_exists_case(C('TMPL_FILE_NAME'))){
  111. // 检查是否存在默认模版 如果有直接输出模版
  112. $this->display();
  113. }else{
  114. // 抛出异常
  115. throw_exception(L('_ERROR_ACTION_').ACTION_NAME);
  116. }
  117. }else{
  118. switch(strtolower($method)) {
  119. // 获取变量 支持过滤和默认值 调用方式 $this->_post($key,$filter,$default);
  120. case '_get': $input =& $_GET;break;
  121. case '_post':$input =& $_POST;break;
  122. case '_put':
  123. case '_delete':parse_str(file_get_contents('php://input'), $input);break;
  124. case '_request': $input =& $_REQUEST;break;
  125. case '_session': $input =& $_SESSION;break;
  126. case '_cookie': $input =& $_COOKIE;break;
  127. case '_server': $input =& $_SERVER;break;
  128. default:
  129. throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  130. }
  131. if(isset($input[$args[0]])) { // 取值操作
  132. $data = $input[$args[0]];
  133. $fun = $args[1]?$args[1]:C('DEFAULT_FILTER');
  134. $data = $fun($data); // 参数过滤
  135. }else{ // 变量默认值
  136. $data = isset($args[2])?$args[2]:NULL;
  137. }
  138. return $data;
  139. }
  140. }
  141. /**
  142. * 模板显示
  143. * 调用内置的模板引擎显示方法,
  144. * @access protected
  145. * @param string $templateFile 指定要调用的模板文件
  146. * 默认为空 由系统自动定位模板文件
  147. * @param string $charset 输出编码
  148. * @param string $contentType 输出类型
  149. * @return void
  150. */
  151. protected function display($templateFile='',$charset='',$contentType='') {
  152. $this->view->display($templateFile,$charset,$contentType);
  153. }
  154. /**
  155. * 模板变量赋值
  156. * @access protected
  157. * @param mixed $name 要显示的模板变量
  158. * @param mixed $value 变量的值
  159. * @return void
  160. */
  161. protected function assign($name,$value='') {
  162. $this->view->assign($name,$value);
  163. }
  164. public function __set($name,$value) {
  165. $this->view->assign($name,$value);
  166. }
  167. /**
  168. * 设置页面输出的CONTENT_TYPE和编码
  169. * @access public
  170. * @param string $type content_type 类型对应的扩展名
  171. * @param string $charset 页面输出编码
  172. * @return void
  173. */
  174. public function setContentType($type, $charset=''){
  175. if(headers_sent()) return;
  176. if(empty($charset)) $charset = C('DEFAULT_CHARSET');
  177. $type = strtolower($type);
  178. if(isset($this->_types[$type])) //过滤content_type
  179. header('Content-Type: '.$this->_types[$type].'; charset='.$charset);
  180. }
  181. /**
  182. * 输出返回数据
  183. * @access protected
  184. * @param mixed $data 要返回的数据
  185. * @param String $type 返回类型 JSON XML
  186. * @param integer $code HTTP状态
  187. * @return void
  188. */
  189. protected function response($data,$type='',$code=200) {
  190. // 保存日志
  191. if(C('LOG_RECORD')) Log::save();
  192. $this->sendHttpStatus($code);
  193. exit($this->encodeData($data,strtolower($type)));
  194. }
  195. /**
  196. * 编码数据
  197. * @access protected
  198. * @param mixed $data 要返回的数据
  199. * @param String $type 返回类型 JSON XML
  200. * @return void
  201. */
  202. protected function encodeData($data,$type='') {
  203. if(empty($data)) return '';
  204. if('json' == $type) {
  205. // 返回JSON数据格式到客户端 包含状态信息
  206. $data = json_encode($data);
  207. }elseif('xml' == $type){
  208. // 返回xml格式数据
  209. $data = xml_encode($data);
  210. }elseif('php'==$type){
  211. $data = serialize($data);
  212. }// 默认直接输出
  213. $this->setContentType($type);
  214. header('Content-Length: ' . strlen($data));
  215. return $data;
  216. }
  217. // 发送Http状态信息
  218. protected function sendHttpStatus($status) {
  219. static $_status = array(
  220. // Informational 1xx
  221. 100 => 'Continue',
  222. 101 => 'Switching Protocols',
  223. // Success 2xx
  224. 200 => 'OK',
  225. 201 => 'Created',
  226. 202 => 'Accepted',
  227. 203 => 'Non-Authoritative Information',
  228. 204 => 'No Content',
  229. 205 => 'Reset Content',
  230. 206 => 'Partial Content',
  231. // Redirection 3xx
  232. 300 => 'Multiple Choices',
  233. 301 => 'Moved Permanently',
  234. 302 => 'Moved Temporarily ', // 1.1
  235. 303 => 'See Other',
  236. 304 => 'Not Modified',
  237. 305 => 'Use Proxy',
  238. // 306 is deprecated but reserved
  239. 307 => 'Temporary Redirect',
  240. // Client Error 4xx
  241. 400 => 'Bad Request',
  242. 401 => 'Unauthorized',
  243. 402 => 'Payment Required',
  244. 403 => 'Forbidden',
  245. 404 => 'Not Found',
  246. 405 => 'Method Not Allowed',
  247. 406 => 'Not Acceptable',
  248. 407 => 'Proxy Authentication Required',
  249. 408 => 'Request Timeout',
  250. 409 => 'Conflict',
  251. 410 => 'Gone',
  252. 411 => 'Length Required',
  253. 412 => 'Precondition Failed',
  254. 413 => 'Request Entity Too Large',
  255. 414 => 'Request-URI Too Long',
  256. 415 => 'Unsupported Media Type',
  257. 416 => 'Requested Range Not Satisfiable',
  258. 417 => 'Expectation Failed',
  259. // Server Error 5xx
  260. 500 => 'Internal Server Error',
  261. 501 => 'Not Implemented',
  262. 502 => 'Bad Gateway',
  263. 503 => 'Service Unavailable',
  264. 504 => 'Gateway Timeout',
  265. 505 => 'HTTP Version Not Supported',
  266. 509 => 'Bandwidth Limit Exceeded'
  267. );
  268. if(isset($_status[$code])) {
  269. header('HTTP/1.1 '.$code.' '.$_status[$code]);
  270. // 确保FastCGI模式下正常
  271. header('Status:'.$code.' '.$_status[$code]);
  272. }
  273. }
  274. /**
  275. * 获取当前请求的Accept头信息
  276. * @return string
  277. */
  278. protected function getAcceptType(){
  279. $type = array(
  280. 'html' => 'text/html,application/xhtml+xml,*/*',
  281. 'xml' => 'application/xml,text/xml,application/x-xml',
  282. 'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
  283. 'js' => 'text/javascript,application/javascript,application/x-javascript',
  284. 'css' => 'text/css',
  285. 'rss' => 'application/rss+xml',
  286. 'yaml' => 'application/x-yaml,text/yaml',
  287. 'atom' => 'application/atom+xml',
  288. 'pdf' => 'application/pdf',
  289. 'text' => 'text/plain',
  290. 'png' => 'image/png',
  291. 'jpg' => 'image/jpg,image/jpeg,image/pjpeg',
  292. 'gif' => 'image/gif',
  293. 'csv' => 'text/csv'
  294. );
  295. foreach($type as $key=>$val){
  296. $array = explode(',',$val);
  297. foreach($array as $k=>$v){
  298. if(stristr($_SERVER['HTTP_ACCEPT'], $v)) {
  299. return $key;
  300. }
  301. }
  302. }
  303. return false;
  304. }
  305. }