// +---------------------------------------------------------------------- defined('THINK_PATH') or exit(); /** * ThinkPHP Restful 控制器扩展 * @category Extend * @package Extend * @subpackage Action * @author liu21st */ abstract class RestAction { // 当前Action名称 private $name = ''; // 视图实例 protected $view = null; protected $_method = ''; // 当前请求类型 protected $_type = ''; // 当前资源类型 // 输出类型 protected $_types = array(); /** * 架构函数 取得模板对象实例 * @access public */ public function __construct() { //实例化视图类 $this->view = Think::instance('View'); if(!defined('__EXT__')) define('__EXT__',''); // 资源类型检测 if(''==__EXT__) { // 自动检测资源类型 $this->_type = $this->getAcceptType(); }elseif(false === stripos(C('REST_CONTENT_TYPE_LIST'),__EXT__)) { // 资源类型非法 则用默认资源类型访问 $this->_type = C('REST_DEFAULT_TYPE'); }else{ // 检测实际资源类型 if($this->getAcceptType() == __EXT__) { $this->_type = __EXT__; }else{ $this->_type = C('REST_DEFAULT_TYPE'); } } // 请求方式检测 $method = strtolower($_SERVER['REQUEST_METHOD']); if(false === stripos(C('REST_METHOD_LIST'),$method)) { // 请求方式非法 则用默认请求方法 $method = C('REST_DEFAULT_METHOD'); } $this->_method = $method; // 允许输出的资源类型 $this->_types = C('REST_OUTPUT_TYPE'); //控制器初始化 if(method_exists($this,'_initialize')) $this->_initialize(); } /** * 获取当前Action名称 * @access protected */ protected function getActionName() { if(empty($this->name)) { // 获取Action名称 $this->name = substr(get_class($this),0,-6); } return $this->name; } /** * 是否AJAX请求 * @access protected * @return boolean */ protected function isAjax() { if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) ) { if('xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) return true; } if(!empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')])) // 判断Ajax方式提交 return true; return false; } /** * 魔术方法 有不存在的操作的时候执行 * @access public * @param string $method 方法名 * @param array $args 参数 * @return mixed */ public function __call($method,$args) { if( 0 === strcasecmp($method,ACTION_NAME)) { if(method_exists($this,$method.'_'.$this->_method.'_'.$this->_type)) { // RESTFul方法支持 $fun = $method.'_'.$this->_method.'_'.$this->_type; $this->$fun(); }elseif($this->_method == C('REST_DEFAULT_METHOD') && method_exists($this,$method.'_'.$this->_type) ){ $fun = $method.'_'.$this->_type; $this->$fun(); }elseif($this->_type == C('REST_DEFAULT_TYPE') && 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(C('TMPL_FILE_NAME'))){ // 检查是否存在默认模版 如果有直接输出模版 $this->display(); }else{ // 抛出异常 throw_exception(L('_ERROR_ACTION_').ACTION_NAME); } }else{ switch(strtolower($method)) { // 获取变量 支持过滤和默认值 调用方式 $this->_post($key,$filter,$default); case '_get': $input =& $_GET;break; case '_post':$input =& $_POST;break; case '_put': case '_delete':parse_str(file_get_contents('php://input'), $input);break; case '_request': $input =& $_REQUEST;break; case '_session': $input =& $_SESSION;break; case '_cookie': $input =& $_COOKIE;break; case '_server': $input =& $_SERVER;break; default: throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); } if(isset($input[$args[0]])) { // 取值操作 $data = $input[$args[0]]; $fun = $args[1]?$args[1]:C('DEFAULT_FILTER'); $data = $fun($data); // 参数过滤 }else{ // 变量默认值 $data = isset($args[2])?$args[2]:NULL; } return $data; } } /** * 模板显示 * 调用内置的模板引擎显示方法, * @access protected * @param string $templateFile 指定要调用的模板文件 * 默认为空 由系统自动定位模板文件 * @param string $charset 输出编码 * @param string $contentType 输出类型 * @return void */ protected function display($templateFile='',$charset='',$contentType='') { $this->view->display($templateFile,$charset,$contentType); } /** * 模板变量赋值 * @access protected * @param mixed $name 要显示的模板变量 * @param mixed $value 变量的值 * @return void */ protected function assign($name,$value='') { $this->view->assign($name,$value); } public function __set($name,$value) { $this->view->assign($name,$value); } /** * 设置页面输出的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->_types[$type])) //过滤content_type header('Content-Type: '.$this->_types[$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) { // 保存日志 if(C('LOG_RECORD')) Log::save(); $this->sendHttpStatus($code); exit($this->encodeData($data,strtolower($type))); } /** * 编码数据 * @access protected * @param mixed $data 要返回的数据 * @param String $type 返回类型 JSON XML * @return void */ protected function encodeData($data,$type='') { if(empty($data)) return ''; if('json' == $type) { // 返回JSON数据格式到客户端 包含状态信息 $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; } // 发送Http状态信息 protected function sendHttpStatus($status) { 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]); } } /** * 获取当前请求的Accept头信息 * @return string */ protected function getAcceptType(){ $type = array( 'html' => 'text/html,application/xhtml+xml,*/*', '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' ); foreach($type as $key=>$val){ $array = explode(',',$val); foreach($array as $k=>$v){ if(stristr($_SERVER['HTTP_ACCEPT'], $v)) { return $key; } } } return false; } }