zentaopms/framework/api/entry.class.php
2023-05-16 10:47:08 +08:00

721 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* 禅道API的entry类。
* The entry class file of ZenTao API.
*
* @package framework
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
class entry extends baseEntry
{
public function __construct()
{
parent::__construct();
if($this->app->action == 'options') throw EndResponseException::create($this->send(204));
if(!isset($this->app->user) or $this->app->user->account == 'guest') throw EndResponseException::create($this->sendError(401, 'Unauthorized'));
$this->dao = $this->loadModel('common')->dao;
}
}
/**
* 禅道API的baseEntry类。
* The baseEntry class file of ZenTao API.
*
*/
class baseEntry
{
/**
* 全局对象 $app。
* The global $app object.
*
* @var object
* @access public
*/
public $app;
/**
* 语言项 $lang。
* The global $app object.
*
* @var object
* @access public
*/
public $lang;
/**
* 提交的POST数据
* The decoded request body.
*
* @var object
* @access public
*/
public $requestBody;
/**
* 构造方法。
* The construct function.
*
* @access public
* @return void
*/
public function __construct()
{
global $app, $config, $lang;
$this->app = $app;
$this->config = $config;
$this->lang = $lang;
$this->parseRequestBody();
}
/**
* 获取请求数据(POST PUT)
* Get request data(POST or PUT)
*
* @param string $key
* @param mixed $defaultValue
* @access public
* @return mixed
*/
public function request($key, $defaultValue = '')
{
if(isset($this->requestBody->$key)) return $this->requestBody->$key;
return $defaultValue;
}
/**
* 获取请求参数
* Get request params.
*
* @param string $key
* @param mixed $defaultValue
* @access public
* @return mixed
*/
public function param($key, $defaultValue = '')
{
if(isset($_GET[$key])) return $_GET[$key];
return $defaultValue;
}
/**
* 设置请求参数
* Set request param.
*
* @param string|array $key if is array, set params by its key-value pairs.
* @param mixed $value
* @access public
* @return void
*/
public function setParam($key, $value = null)
{
if(is_array($key))
{
foreach($key as $k => $v) $_GET[$k] = $v;
return;
}
$_GET[$key] = $value;
}
/**
* 解析请求数据
* Parse body of request data.
*
* @access public
* @return void
*/
private function parseRequestBody()
{
$this->requestBody = new stdClass();
if($this->app->action == 'post' or $this->app->action == 'put')
{
$requestBody = file_get_contents("php://input");
if($requestBody) $this->requestBody = json_decode($requestBody);
}
}
/**
* HTTP状态码
* HTTP status code
*
* @access public
*/
public $statusCode = array(
100 => "100 Continue",
101 => "101 Switching Protocols",
102 => "102 Processing",
200 => "200 OK",
201 => "201 Created",
202 => "202 Accepted",
203 => "203 Non-Authoritative Information",
204 => "204 No Content",
205 => "205 Reset Content",
206 => "206 Partial Content",
207 => "207 Multi-Status",
300 => "300 Multiple Choices",
301 => "301 Moved Permanently",
302 => "302 Found",
303 => "303 See Other",
304 => "304 Not Modified",
305 => "305 Use Proxy",
307 => "307 Temporary Redirect",
400 => "400 Bad Request",
401 => "401 Authorization Required",
402 => "402 Payment Required",
403 => "403 Forbidden",
404 => "404 Not Found",
405 => "405 Method Not Allowed",
406 => "406 Not Acceptable",
407 => "407 Proxy Authentication Required",
408 => "408 Request Time-out",
409 => "409 Conflict",
410 => "410 Gone",
411 => "411 Length Required",
412 => "412 Precondition Failed",
413 => "413 Request Entity Too Large",
414 => "414 Request-URI Too Large",
415 => "415 Unsupported Media Type",
416 => "416 Requested Range Not Satisfiable",
417 => "417 Expectation Failed",
422 => "422 Unprocessable Entity",
423 => "423 Locked",
424 => "424 Failed Dependency",
426 => "426 Upgrade Required",
);
/**
* 发送请求的响应数据
* Send response data
*
* @param int $code
* @param mixed $data
* @access public
* @return string
*/
public function send($code, $data = '')
{
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,Authorization,Token,Referer,User-Agent");
header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH');
header("Content-type: application/json");
header("HTTP/1.1 {$this->statusCode[$code]}");
return !empty($data) ? json_encode($data, JSON_HEX_TAG) : '';
}
/**
* 发送错误信息
* Send error response
*
* @param int $code
* @param string $msg
* @access public
* @return string
*/
public function sendError($code, $msg)
{
$response = new stdclass();
$response->error = $msg;
return $this->send($code, $response);
}
/**
* 发送成功提示
* Send success response
*
* @param int $code
* @param string $msg
* @access public
* @return string
*/
public function sendSuccess($code, $msg)
{
$response = new stdclass();
$response->message = $msg;
return $this->send($code, $response);
}
/**
* Send 400 response.
*
* @param string message
* @access public
* @return string
*/
public function send400($message = 'error')
{
return $this->sendError(400, $message);
}
/**
* Send 404 response.
*
* @access public
* @return string
*/
public function send404()
{
return $this->sendError(404, '404 Not found');
}
/**
* 加载禅道的控制器类
* Load controller of zentaopms
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return object
*/
public function loadController($moduleName, $methodName)
{
ob_start();
if(!class_exists($moduleName) and !class_exists("my$moduleName"))
{
global $app;
$app->setModuleName($moduleName);
$app->setMethodName($methodName);
$app->viewType = 'json';
/* Check user permission. */
$this->checkPriv();
$app->setControlFile();
/*
* 引入该模块的control文件。
* Include the control file of the module.
**/
$isExt = $app->setActionExtFile();
if($isExt)
{
$controlFile = $app->controlFile;
spl_autoload_register(function($class) use ($moduleName, $controlFile)
{
if($class == $moduleName) include $controlFile;
});
}
$file2Included = $isExt ? $app->extActionFile : $app->controlFile;
chdir(dirname($file2Included));
helper::import($file2Included);
}
/*
* 设置control的类名。
* Set the class name of the control.
**/
$className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
if(!class_exists($className)) $app->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true);
$controller = new $className();
$controller->viewType = 'json';
return $controller;
}
/**
* 加载指定模块的model文件。
* Load the model file of one module.
*
* @param string $moduleName 模块名如果为空使用当前模块。The module name, if empty, use current module's name.
* @param string $appName The app name, if empty, use current app's name.
* @access public
* @return object|bool 如果没有model文件返回false否则返回model对象。If no model file, return false, else return the model object.
*/
public function loadModel($moduleName = '', $appName = '')
{
if(empty($moduleName)) $moduleName = $this->app->moduleName;
if(empty($appName)) $appName = $this->app->appName;
global $loadedModels;
if(isset($loadedModels[$appName][$moduleName]))
{
$this->$moduleName = $loadedModels[$appName][$moduleName];
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
}
$modelFile = $this->app->setModelFile($moduleName, $appName);
/**
* 如果没有model文件尝试加载config配置信息。
* If no model file, try load config.
*/
if(!helper::import($modelFile))
{
$this->app->loadModuleConfig($moduleName, $appName);
$this->app->loadLang($moduleName, $appName);
$this->dao = new dao();
return false;
}
/**
* 如果没有扩展文件model类名是$moduleName + 'model'如果有扩展还需要增加ext前缀。
* If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix.
*/
$modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model';
if(!class_exists($modelClass))
{
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
}
/**
* 初始化model对象在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量方便引用。
* Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object.
*/
$loadedModels[$appName][$moduleName] = new $modelClass($appName);
$this->$moduleName = $loadedModels[$appName][$moduleName];
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
}
/**
* 获取控制器执行返回的数据在output缓存中.
* Get controller data from output.
*
* @access public
* @return object.
*/
public function getData()
{
$output = helper::removeUTF8Bom(ob_get_clean());
$output = json_decode($output);
if(isset($output->data)) $output->data = json_decode($output->data);
return $output;
}
/**
* 添加$_POST全局变量.
* Add data to $_POST.
*
* @param string $key
* @param mixed $value
* @access public
* @return void
*/
public function setPost($key, $value)
{
$_POST[$key] = $value;
}
/**
* 批量添加$_POST全局变量.
* Batch set data to $_POST.
*
* @param string $fields
* @param mixed $object
* @access public
* @return void
*/
public function batchSetPost($fields, $object = '')
{
$fields = explode(',', $fields);
foreach($fields as $field)
{
/*
* If the field exists in request body, use it.
* Otherwise set default value from $object.
*/
if(isset($this->requestBody->$field))
{
$value = $this->requestBody->$field;
}
else
{
if(!$object or !isset($object->$field)) continue;
$value = $object->$field;
}
$this->setPost($field, $value);
}
}
/**
* 确保字段不能为空.
* Make sure the fields is not empty.
*
* @param string $fields
* @param mixed $object
* @access public
* @return void
*/
public function requireFields($fields)
{
$fields = explode(',', $fields);
foreach($fields as $field)
{
if(!isset($_POST[$field]))
{
$module = $this->app->moduleName;
$name = isset($this->app->lang->$module->$field) ? $this->app->lang->$module->$field : $field;
throw EndResponseException::create($this->sendError(400, sprintf($this->app->lang->error->notempty, $name)));
}
}
}
/**
* 格式化数据的字段类型.
* Format fields of response data.
*
* @param object|array $data
* @param string $fields
* @access public
* @return object|array
*/
public function format($data, $fields)
{
if(is_array($data))
{
foreach($data as $object) $this->formatFields($object, $fields);
}
$this->formatFields($data, $fields);
return $data;
}
/**
* 格式化对象的字段类型.
* Format fields of object.
*
* @param object $object
* @param string $fields
* @access public
* @return object
*/
private function formatFields(&$object, $fields)
{
$fields = explode(',', $fields);
foreach($fields as $field)
{
$field = explode(':', $field);
$key = $field[0];
$type = $field[1];
$isArray = false;
if(!isset($object->$key)) continue;
$pos = strpos($type, ']');
if($pos !== false)
{
$isArray = true;
$type = substr($type, $pos + 1);
}
else if(strpos($type, 'array') !== false)
{
$isArray = true;
$type = 'object';
}
/* Format value. */
if(!$isArray)
{
$object->$key = $this->cast(trim($object->$key, ','), $type);
continue;
}
/* Format array. */
$value = array();
if(is_array($object->$key) or is_object($object->$key))
{
foreach($object->$key as $v) $value[] = $this->cast($v, $type);
}
else
{
$vs = implode(',', $object->$key);
foreach($vs as $v)
{
if($v === '') continue;
$value[] = $this->cast($v, $type);
}
}
$object->$key = $value;
}
}
/**
* Filter fields.
*
* @param object $object
* @param array $filters
* @access public
* @return object
*/
public function filterFields($object, $allowable = '')
{
if(empty($allowable)) return $object;
if(is_string($allowable)) $allowable = explode(',', $allowable);
$filtered = new stdclass();
foreach($allowable as $field)
{
$field = trim($field);
if(empty($field)) continue;
if(!isset($object->$field)) continue;
$filtered->$field = $object->$field;
}
return $filtered;
}
/**
* Format user.
*
* @param string $account
* @param array $users
* @access public
* @return array
*/
public function formatUser($account, $users)
{
$user = array();
$user['account'] = $account;
$user['realname'] = zget($users, $account);
return $user;
}
/**
* 类型转换.
* Typecasting.
*
* @param mixed $vaule
* @param string $type
* @access public
* @return mixed
*/
private function cast($value, $type)
{
switch($type)
{
case 'time':
$timeFormat = $this->param('timeFormat', 'utc');
if($timeFormat == 'utc')
{
if(!$value or $value == '0000-00-00 00:00:00') return null;
return gmdate("Y-m-d\TH:i:s\Z", strtotime($value));
}
return $value;
case 'date':
if(!$value or $value == '0000-00-00') return null;
return $value;
case 'bool':
return !empty($value);
case 'int':
return (int) $value;
case 'idList':
$values = explode(',', $value);
if(empty($values)) return array();
$idList = array();
foreach($values as $val)
{
if($val !== '') $idList[] = (int) $val;
}
return $idList;
case 'stringList':
$values = explode(',', $value);
if(empty($values)) return array();
$stringList = array();
foreach($values as $val)
{
if($val !== '') $stringList[] = $val;
}
return $stringList;
case 'array':
$array = array();
if(!empty($value)) foreach($value as $v) $array[] = $v;
return $array;
case 'user':
if(empty($value)) return null;
if(empty($this->users)) $this->users = $this->dao->select('id,account,avatar,realname')->from(TABLE_USER)->fetchAll('account');
return zget($this->users, $value, null);
case 'userList':
$values = explode(',', $value);
if(empty($values)) return array();
$userList = array();
foreach($values as $val)
{
$val = $this->cast($val, 'user');
if($val) $userList[] = $val;
}
return $userList;
default:
return $value;
}
}
/**
* 获取其他方法的执行结果。
* Fetch result of other method.
*
* @param string $entry
* @param string $method
* @param array $params
* @access public
* @return mixed
*/
public function fetch($entry, $method, $params = array())
{
include($this->app->appRoot . "api/{$this->app->version}/entries/" . strtolower($entry) . ".php");
$entryName = $entry . 'Entry';
$entry = new $entryName();
return call_user_func_array(array($entry, $method), $params);
}
/**
* Check the user has permission to access this method, if not, return 403.
*
* @access public
* @return void|string
*/
public function checkPriv()
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($module and $method and !$this->loadModel('common')->isOpenMethod($module, $method) and !commonModel::hasPriv($module, $method))
{
return $this->send(403, array('error' => 'Access not allowed'));
}
}
/**
* Reset open app.
*
* @param string $tab
* @access public
* @return void
*/
public function resetOpenApp($tab)
{
$_COOKIE['tab'] = $tab;
$this->app->tab = $tab;
$this->app->session->tab = $tab;
}
}