<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- namespace Think\Controller; use Think\Controller; use Think\Build; /** * ThinkPHP REST控制器类 */ class RestController extends Controller { // 当前请求类型 protected $_method = ''; // 当前请求的资源类型 protected $_type = ''; // REST允许的请求类型列表 protected $allowMethod = array( 'get', 'post', 'put', 'delete' ); // REST默认请求类型 protected $defaultMethod = 'get'; // REST允许请求的资源类型列表 protected $allowType = array( 'html', 'xml', 'json', 'rss' ); // 默认的资源类型 protected $defaultType = 'html'; // REST允许输出的资源类型列表 protected $allowOutputType = array( 'xml' => 'application/xml', 'json' => 'application/json', 'html' => 'text/html' ); /** * 架构函数 * * @access public */ public function __construct() { // 资源类型检测 if ('' == __EXT__) { // 自动检测资源类型 $this->_type = $this->getAcceptType(); } elseif (! in_array(__EXT__, $this->allowType)) { // 资源类型非法 则用默认资源类型访问 $this->_type = $this->defaultType; } else { $this->_type = __EXT__; } // 请求方式检测 $method = strtolower(REQUEST_METHOD); if (! in_array($method, $this->allowMethod)) { // 请求方式非法 则用默认请求方法 $method = $this->defaultMethod; } $this->_method = $method; parent::__construct(); } /** * 魔术方法 有不存在的操作的时候执行 * * @access public * * @param string $method 方法名 * @param array $args 参数 * * @return mixed */ public function __call($method, $args) { if (0 === strcasecmp($method, ACTION_NAME . C('ACTION_SUFFIX'))) { if (method_exists($this, $method . '_' . $this->_method . '_' . $this->_type)) { // RESTFul方法支持 $fun = $method . '_' . $this->_method . '_' . $this->_type; $this->$fun(); } elseif ($this->_method == $this->defaultMethod && method_exists($this, $method . '_' . $this->_type)) { $fun = $method . '_' . $this->_type; $this->$fun(); } elseif ($this->_type == $this->defaultType && method_exists($this, $method . '_' . $this->_method)) { $fun = $method . '_' . $this->_method; $this->$fun(); } elseif (method_exists($this, '_empty')) { // 如果定义了_empty操作 则调用 $this->_empty($method, $args); } elseif (file_exists_case($this->view->parseTemplate())) { // 检查是否存在默认模版 如果有直接输出模版 $this->display(); } else { if ($this->_build_action()) { // 报生成成功信息 E(__CLASS__ . ':' . $method . '_' . $this->_method . L('METHOD_CREATED')); } E(L('_ERROR_ACTION_') . ':' . ACTION_NAME); } } } // by zhuxun begin. // 创建不存在的方法 protected function _build_action() { if (APP_DEBUG && cfg('CREATE_ACTION_ON') && I('request._create')) { // 获取类名 $classname = get_class($this); // 默认控制层名称 $c_layer = ucfirst(C('DEFAULT_C_LAYER')); $classname = preg_replace("/" . $c_layer . "$/is", '', $classname); // 切分路径 $paths = explode("/", str_replace("\\", "/", $classname)); // 获取模块名称 $module = array_shift($paths); // 剔除控制层目录 array_shift($paths); // 重新拼接控制层 $controller = implode("/", $paths); // 创建操作方法 Build::buildAction($module, $controller, ACTION_NAME, true); return true; } return false; } /** * 显示异常 * * @param mixed $data 异常信息 * @param string $tpl 模板 * * @return bool */ public function showException($data = null, $tpl = '') { $this->_response($data); return true; } // end. /** * 获取当前请求的Accept头信息 * * @return string */ protected function getAcceptType() { $type = array( 'xml' => 'application/xml,text/xml,application/x-xml', 'json' => 'application/json,text/x-json,application/jsonrequest,text/json', 'js' => 'text/javascript,application/javascript,application/x-javascript', 'css' => 'text/css', 'rss' => 'application/rss+xml', 'yaml' => 'application/x-yaml,text/yaml', 'atom' => 'application/atom+xml', 'pdf' => 'application/pdf', 'text' => 'text/plain', 'png' => 'image/png', 'jpg' => 'image/jpg,image/jpeg,image/pjpeg', 'gif' => 'image/gif', 'csv' => 'text/csv', 'html' => 'text/html,application/xhtml+xml,*/*' ); foreach ($type as $key => $val) { $array = explode(',', $val); foreach ($array as $k => $v) { if (isset($_SERVER['HTTP_ACCEPT']) && stristr($_SERVER['HTTP_ACCEPT'], $v)) { return $key; } } } return false; } // 发送Http状态信息 protected function sendHttpStatus($code) { static $_status = array( // Informational 1xx 100 => 'Continue', 101 => 'Switching Protocols', // Success 2xx 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', // Redirection 3xx 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Moved Temporarily ', // 1.1 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', // 306 is deprecated but reserved 307 => 'Temporary Redirect', // Client Error 4xx 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', // Server Error 5xx 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 509 => 'Bandwidth Limit Exceeded' ); if (isset($_status[$code])) { header('HTTP/1.1 ' . $code . ' ' . $_status[$code]); // 确保FastCGI模式下正常 header('Status:' . $code . ' ' . $_status[$code]); } } /** * 编码数据 * * @access protected * * @param mixed $data 要返回的数据 * @param String $type 返回类型 JSON XML * * @return string */ protected function encodeData($data, $type = '') { if (empty($data)) { return ''; } if ('json' == $type) { // 返回JSON数据格式到客户端 包含状态信息 if (PHP_VERSION > '5.5') { $data = json_encode($data, JSON_UNESCAPED_UNICODE|JSON_PARTIAL_OUTPUT_ON_ERROR); } elseif (PHP_VERSION > '5.4') { $data = json_encode($data, JSON_UNESCAPED_UNICODE); } else { $data = json_encode($data); } } elseif ('xml' == $type) { // 返回xml格式数据 $data = xml_encode($data); } elseif ('php' == $type) { $data = serialize($data); } // 默认直接输出 $this->setContentType($type); // header('Content-Length: ' . strlen($data)); return $data; } /** * 设置页面输出的CONTENT_TYPE和编码 * * @access public * * @param string $type content_type 类型对应的扩展名 * @param string $charset 页面输出编码 * * @return void */ public function setContentType($type, $charset = '') { if (headers_sent()) { return; } if (empty($charset)) { $charset = C('DEFAULT_CHARSET'); } $type = strtolower($type); if (isset($this->allowOutputType[$type])) { // 过滤content_type header('Content-Type: ' . $this->allowOutputType[$type] . '; charset=' . $charset); } } /** * 输出返回数据 * * @access protected * * @param mixed $data 要返回的数据 * @param String $type 返回类型 JSON XML * @param integer $code HTTP状态 * * @return void */ protected function _response($data, $type = '', $code = 200) { $this->sendHttpStatus($code); exit($this->encodeData($data, strtolower($type))); } }