2205 lines
58 KiB
PHP
2205 lines
58 KiB
PHP
<?php
|
||
/**
|
||
* Created by Notepad++.
|
||
* User: 应俊
|
||
* Date: 2016-08-08
|
||
* Time: 11:22
|
||
*/
|
||
|
||
|
||
/***********************************************************************************************************************
|
||
* 2016-08-11:
|
||
* 原请求参数中的user_auth_token和app_auth_token参数改名为user_auth_token_1和app_auth_token_1表示自用。
|
||
* 新增请求参数user_auth_token_2和app_auth_token_2用来对接其他接口(暂时对接阿里口碑)。
|
||
* 2016-08-30:
|
||
* 增加权限判断。
|
||
* 2016-09-12:
|
||
* 增加signature。
|
||
* 2016-11-29:
|
||
* 增加mssql和oracle的支持
|
||
* 2016-12-17:
|
||
* PDO_Insert, PDO_Update 的字段值允许指定raw属性,如果raw为true则不以参数形式传递。
|
||
* PDO_Insert, PDO_Update 的字段值允许指定type属性,根据不同的type拼装不同的字段描述字符。
|
||
* 所有的where条件都支持raw和type属性处理.
|
||
* 2017-02-17:
|
||
* 增加PDO_Select_Page_2方法,用于支持多表关联查询时,只根据一张表来分页的需求。
|
||
* 该方法默认只支持内连接方式。如需要左或右连接,需要在查询条件中指明(mysql不支持)
|
||
* 例:
|
||
* oracle中: 表1.字段1 = 表2.字段2(+) 等价 表1 left join 表2 on 表1.字段1 = 表2.字段2
|
||
* sqlserver中: 表1.字段1 *= 表2.字段2 等价 表1 left join 表2 on 表1.字段1 = 表2.字段2
|
||
* mysql中: mysql 不支持隐含 xxx join 的写法,所以使用该方法时只能适配内连接方式。
|
||
* 2017-03-03:
|
||
* 增加PDO_Select、PDO_Select_Page方法对多表的支持。
|
||
* 增加GetSelectTables方法来处理多表表名。
|
||
* 在GetSelectFields中,增加对包含.的字段名的处理(包含.则两边增加限定符)
|
||
* 2017-03-31:
|
||
* 查询方法增加group的支持.
|
||
* 2017-04-01:
|
||
* 修复了若干bug,并且把BaseMethod类独立到BaseMethodHelper.php以增加可读性。
|
||
* 2017-06-28:
|
||
* 去掉了app_id, developer_id, user_auth_token_1, app_auth_token_1, user_auth_token_2, app_auth_token_2以及相关校验。
|
||
* 2017-09-25:
|
||
* 增加了redis的支持,用来控制接口访问的安全控制。
|
||
* 首次调用:
|
||
* 1:客户机登录
|
||
* 2:生成token(格式:sessionid={"RequestUser":"请求的用户","RequestName":"请求的关键字","RequestAddr":"请求的地址","RequestTime":"请求的时间"})
|
||
* 3:返回给下次请求使用
|
||
* 再次调用:
|
||
* 1:客户机发送上次返回的token
|
||
* 2:校验token(校验本次时间呵上次保存的时间的间隔是否大于预定的时间间隔)
|
||
* 3:删除原有token
|
||
* 4:生成token(格式:sessionid={"RequestUser":"请求的用户","RequestName":"请求的关键字","RequestAddr":"请求的地址","RequestTime":"请求的时间"})
|
||
* 5:返回给下次请求使用
|
||
* 2017-10-16:
|
||
* 增加了对登录接口访问频率和两次请求客户端地址的限制。
|
||
* 2017-12-05:
|
||
* 增加随机字符串参数random_string用于解决缓存问题。
|
||
* 2017-12-25:
|
||
* 增加一个标志,用来开启或关闭token的校验
|
||
* 2017-12-27:
|
||
* 增加一个标志,用来限制token是否只能单次使用
|
||
* 2018-03-20:
|
||
* OpenAPITrunk中VerifyMethod里,增加对token信息中requestuser域和request中user_id参数的校验
|
||
***********************************************************************************************************************/
|
||
|
||
|
||
//require_once __DIR__ . '/CommonConstant.php';
|
||
require_once __DIR__ . '/DatabaseHelper.php';
|
||
require_once __DIR__ . '/config.inc.php';
|
||
|
||
|
||
define('AU_KEY', '2347adfas……&*(');
|
||
define('TYPE_ENCODE', 'ENCODE');
|
||
define('TYPE_DECODE', 'DECODE');
|
||
|
||
/**
|
||
* @param string $string 要加密或解密的字符串
|
||
* @param string $type 加密或解密(DECODE=解密,ENCODE=加密)
|
||
* @param string $key 密匙
|
||
* @param int $expiry 密文有效期
|
||
* @return string
|
||
* @throws Exception
|
||
*/
|
||
function CreateAuthCode($string, $type = TYPE_DECODE, $key = null, $expiry = 0)
|
||
{
|
||
if (!in_array($type, array(TYPE_DECODE, TYPE_ENCODE,)))
|
||
return '';
|
||
|
||
/// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
|
||
$ckey_length = 4;
|
||
|
||
/// 密匙
|
||
$key = md5(empty($key) ? AU_KEY : $key);
|
||
|
||
/// 密匙a会参与加解密
|
||
$keya = md5(substr($key, 0, 16));
|
||
/// 密匙b会用来做数据完整性验证
|
||
$keyb = md5(substr($key, 16, 16));
|
||
/// 密匙c用于变化生成的密文
|
||
$keyc = $ckey_length ? ($type == TYPE_DECODE ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
|
||
/// 参与运算的密匙
|
||
$cryptkey = $keya . md5($keya . $keyc);
|
||
$key_length = strlen($cryptkey);
|
||
|
||
/// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
|
||
/// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
|
||
$string = $type == TYPE_DECODE ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
|
||
$string_length = strlen($string);
|
||
$result = '';
|
||
$box = range(0, 255);
|
||
$rndkey = array();
|
||
|
||
/// 产生密匙簿
|
||
for ($i = 0; $i <= 255; $i++)
|
||
{
|
||
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
|
||
}
|
||
|
||
/// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
|
||
for ($j = $i = 0; $i < 256; $i++)
|
||
{
|
||
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
|
||
$tmp = $box[$i];
|
||
$box[$i] = $box[$j];
|
||
$box[$j] = $tmp;
|
||
}
|
||
|
||
/// 核心加解密部分
|
||
for ($a = $j = $i = 0; $i < $string_length; $i++)
|
||
{
|
||
$a = ($a + 1) % 256;
|
||
$j = ($j + $box[$a]) % 256;
|
||
$tmp = $box[$a];
|
||
$box[$a] = $box[$j];
|
||
$box[$j] = $tmp;
|
||
// 从密匙簿得出密匙进行异或,再转成字符
|
||
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
|
||
}
|
||
|
||
if ($type == TYPE_DECODE)
|
||
{
|
||
/// substr($result, 0, 10) == 0 验证数据有效性
|
||
/// substr($result, 0, 10) - time() > 0 验证数据有效性
|
||
/// substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
|
||
/// 验证数据有效性,请看未加密明文的格式
|
||
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
|
||
return substr($result, 26);
|
||
else
|
||
return '';
|
||
}
|
||
else
|
||
{
|
||
/// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
|
||
/// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
|
||
return $keyc . str_replace('=', '', base64_encode($result));
|
||
}
|
||
}
|
||
|
||
|
||
function EncodeAuthCode($String, $Key = null, $Expiry = 0)
|
||
{
|
||
return CreateAuthCode($String, TYPE_ENCODE, $Key, $Expiry);
|
||
}
|
||
|
||
function DecodeAuthCode($String, $Key = null)
|
||
{
|
||
return CreateAuthCode($String, TYPE_DECODE, $Key);
|
||
}
|
||
|
||
|
||
/**
|
||
* RSA签名
|
||
* @param string $data 待签名数据
|
||
* @param string $private_key_path 商户私钥文件路径
|
||
* @return bool 签名结果
|
||
*/
|
||
function rsaSign($data, $private_key_path)
|
||
{
|
||
$priKey = file_get_contents($private_key_path);
|
||
$res = openssl_get_privatekey($priKey);
|
||
openssl_sign($data, $sign, $res);
|
||
openssl_free_key($res);
|
||
//base64编码
|
||
$sign = base64_encode($sign);
|
||
return $sign;
|
||
}
|
||
|
||
/**
|
||
* RSA验签
|
||
* @param string $data 待签名数据
|
||
* @param string $ali_public_key_path 支付宝的公钥文件路径
|
||
* @param string $sign 要校对的的签名结果
|
||
* @return bool 验证结果
|
||
*/
|
||
function rsaVerify($data, $ali_public_key_path, $sign)
|
||
{
|
||
$pubKey = file_get_contents($ali_public_key_path);
|
||
$res = openssl_get_publickey($pubKey);
|
||
$result = (bool)openssl_verify($data, base64_decode($sign), $res);
|
||
openssl_free_key($res);
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* RSA解密
|
||
* @param string $content 需要解密的内容,密文
|
||
* @param string $private_key_path 商户私钥文件路径
|
||
* @return bool 解密后内容,明文
|
||
*/
|
||
function rsaDecrypt($content, $private_key_path)
|
||
{
|
||
$priKey = file_get_contents($private_key_path);
|
||
$res = openssl_get_privatekey($priKey);
|
||
//用base64将内容还原成二进制
|
||
$content = base64_decode($content);
|
||
//把需要解密的内容,按128位拆开解密
|
||
$result = '';
|
||
for ($i = 0; $i < strlen($content) / 128; $i++)
|
||
{
|
||
$data = substr($content, $i * 128, 128);
|
||
openssl_private_decrypt($data, $decrypt, $res);
|
||
$result .= $decrypt;
|
||
}
|
||
openssl_free_key($res);
|
||
return $result;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @date 2017-03-04
|
||
* @note 给参数按key的顺序排序。(支持递归)
|
||
* @param mixed $parameter 要排序的参数
|
||
* @return array
|
||
* @auth 应俊
|
||
*/
|
||
function SortParam($parameter)
|
||
{
|
||
$parameter = (array)$parameter;
|
||
foreach ($parameter as $k => $v)
|
||
{
|
||
if (is_array($v) || is_object($v))
|
||
{
|
||
$parameter[$k] = SortParam($v);
|
||
}
|
||
}
|
||
|
||
/// 调用strcmp函数来排序,该函数区分大小写。
|
||
uksort($parameter, 'strcmp');
|
||
return $parameter;
|
||
}
|
||
|
||
/**
|
||
* @date 2017-03-06
|
||
* @note 转换参数成字符串形式按key=value的形式,用&分隔)。
|
||
* @param mixed $parameter 要转换的参数
|
||
* @return string
|
||
* @auth 应俊
|
||
*/
|
||
function ConvertParam($parameter)
|
||
{
|
||
$parameter = (array)$parameter;
|
||
$return = '';
|
||
foreach ($parameter as $k => $v)
|
||
{
|
||
if (is_array($v) || is_object($v))
|
||
$return .= sprintf('&%s={%s}', $k, ConvertParam($v));
|
||
else
|
||
$return .= sprintf('&%s=%s', $k, $v);
|
||
}
|
||
|
||
$sublen = mb_strlen('&', USEDCHARSET);
|
||
$retlen = mb_strlen($return, USEDCHARSET);
|
||
$return = mb_substr($return, $sublen, $retlen - $sublen, USEDCHARSET);
|
||
return $return;
|
||
}
|
||
|
||
/**
|
||
* @date 2017-03-04
|
||
* @note 为参数生成签名
|
||
* @param mixed $parameter 要签名的参数
|
||
* @param string $rand 随机字符串
|
||
* @param string $key 签名key
|
||
* @return string
|
||
* @auth 应俊
|
||
*/
|
||
function SignParameter($parameter, $rand = '', $key = '')
|
||
{
|
||
/// 1:先把参数按参数名(key)从小到大排序
|
||
$parameter = SortParam($parameter);
|
||
|
||
/// 2:连接参数成一个字符串(按key=value的形式,用&分隔)。
|
||
$return = ConvertParam($parameter);
|
||
|
||
/// 3:结尾加上key=签名key
|
||
$return .= '&randstring=' . $rand . '&key=' . $key;
|
||
|
||
/// 4:md5加密这个字符串
|
||
return md5($return);
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @note
|
||
* @param mixed $message
|
||
* @return bool
|
||
*/
|
||
function OutputDebugMessage($message)
|
||
{
|
||
if (is_object($message) || is_array($message))
|
||
$message = JsonObjectToJsonString($message);
|
||
|
||
$remoteaddress = $_SERVER['REMOTE_ADDR'];
|
||
$nowdate = date('Y-m-d');
|
||
$nowtime = date('H:i:s');
|
||
$pathname = Characet(dirname($_SERVER['SCRIPT_FILENAME']) . '/debug', 'gbk');
|
||
$filename = "{$pathname}/{$nowdate}.log";
|
||
$message = Characet($message, 'gbk');
|
||
|
||
if (!is_dir($pathname))
|
||
mkdir($pathname, 0777, true);
|
||
|
||
if ($file = fopen($filename, 'a+'))
|
||
{
|
||
if (mb_strstr($message, PHP_EOL, false, USEDCHARSET) != PHP_EOL)
|
||
$message .= PHP_EOL;
|
||
|
||
fwrite($file, "{$nowtime}({$remoteaddress}) ====> {$message}");
|
||
fclose($file);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @param mixed $data
|
||
* @return array|mixed
|
||
*/
|
||
function ChangePostData($data)
|
||
{
|
||
switch (gettype($data))
|
||
{
|
||
case 'array':
|
||
{
|
||
foreach ($data as $key => $value)
|
||
{
|
||
$data[$key] = ChangePostData($value);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'object':
|
||
{
|
||
$array = (array)$data;
|
||
foreach ($array as $key => $value)
|
||
{
|
||
$data->$key = ChangePostData($value);
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
{
|
||
//$data = preg_replace_callback('/\+/', function($r) { return '%2B'; }, $data);
|
||
//$data = preg_replace_callback('/\&/', function($r) { return '%26'; }, $data);
|
||
//$data = preg_replace_callback('/[\+|\&]/', function ($matches) { switch($matches[0]) {case '+': return '%2b';case '&': return '%26'; default: return $matches[0];} }, $data);
|
||
$data = str_replace('\+', '%2B', $data);
|
||
$data = str_replace('\&', '%26', $data);
|
||
}
|
||
break;
|
||
}
|
||
|
||
return $data;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 发送post请求
|
||
* @param string $url
|
||
* @param mixed $data
|
||
* @return string
|
||
*/
|
||
function SendPost($url, $data)
|
||
{
|
||
if (is_object($data) || is_array($data))
|
||
$data = http_build_query(ChangePostData($data));
|
||
else
|
||
$data = ChangePostData($data);
|
||
|
||
$opts = array(
|
||
'http' => array(
|
||
'method' => 'POST',
|
||
'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",
|
||
'content' => $data,)
|
||
);
|
||
|
||
$context = @stream_context_create($opts);
|
||
$ret = @file_get_contents($url, false, $context);
|
||
$ret = trim($ret, "\xEF\xBB\xBF");
|
||
return $ret;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 转换分隔符
|
||
* @param string $string
|
||
* @return mixed
|
||
*/
|
||
function ConvertSeparator($string)
|
||
{
|
||
$result = $string;
|
||
|
||
while (false !== mb_stripos($result, '\\', 0, USEDCHARSET))
|
||
{
|
||
$result = str_replace('\\', '/', $result);
|
||
}
|
||
|
||
while (false !== mb_stripos($result, '//', 0, USEDCHARSET))
|
||
{
|
||
$result = str_replace('//', '/', $result);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* @note 获取文件的url
|
||
* @param string $filename
|
||
* @return string
|
||
*/
|
||
function GetFileUri($filename = __FILE__)
|
||
{
|
||
//echo $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['SCRIPT_NAME'];
|
||
//$filename = __FILE__;
|
||
$filename = ConvertSeparator($filename);
|
||
$pathname = ConvertSeparator(isset($_SERVER['CONTEXT_DOCUMENT_ROOT']) ? $_SERVER['CONTEXT_DOCUMENT_ROOT'] : '');
|
||
$pathlength = mb_strlen($pathname, USEDCHARSET);
|
||
$filelength = mb_strlen($filename, USEDCHARSET);
|
||
|
||
if (false !== stripos($filename, $pathname))
|
||
$objectname = mb_substr($filename, $pathlength, $filelength - $pathlength, USEDCHARSET);
|
||
else
|
||
$objectname = $filename;
|
||
|
||
$https =(isset($_SERVER['REQUEST_SCHEME']) && strcasecmp($_SERVER['REQUEST_SCHEME'], 'https') == 0) ||
|
||
(isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') == 0 || strcasecmp($_SERVER['HTTPS'], '1') == 0));
|
||
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 0;
|
||
$servername = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||
|
||
if ($https)
|
||
$uri = 'https://' . $servername . (443 == $port ? '' : ':' . $port) . $objectname;
|
||
else
|
||
$uri = 'http://' . $servername . (80 == $port ? '' : ':' . $port) . $objectname;
|
||
|
||
return $uri;
|
||
}
|
||
|
||
//echo mb_trim($string, '\s".');
|
||
function mb_trim($string, $trim_chars = " \t\n\r\0\x0B"/*'\s'*/)
|
||
{
|
||
return preg_replace('/^[' . $trim_chars . ']*(?U)(.*)[' . $trim_chars . ']*$/u', '\\1', $string);
|
||
}
|
||
|
||
/**
|
||
* @note 注册错误处理函数
|
||
*/
|
||
function error_reg()
|
||
{
|
||
$ar = array(E_ERROR => 'error', E_WARNING => 'warning', E_PARSE => 'prase', E_NOTICE => 'notice', );
|
||
register_shutdown_function(function () use ($ar)
|
||
{
|
||
$ers = error_get_last();
|
||
if ($ers['type'] != 8 && $ers['type'])
|
||
{
|
||
$er = $ar[$ers['type']] . $ers['type'] . ': ' . ' ' . $ers['message'] . ' => ' . $ers['file'] . ' line:' . $ers['line'] . ' ' . date('Y-m-d H:i:s') . "\n";
|
||
error_log($er, 3, '/tmp/php_error.log');
|
||
}
|
||
});
|
||
|
||
set_error_handler(function ($a, $b, $c, $d) use ($ar)
|
||
{
|
||
if ($a != 8 && $a)
|
||
{
|
||
$er = $ar[$a] . $a . ': ' . $b . ' => ' . $c . ' line:' . $d . ' ' . date('Y-m-d H:i:s') . "\n";
|
||
error_log($er, 3, '/tmp/php_error.log');
|
||
}
|
||
}, E_ALL ^ E_NOTICE);
|
||
}
|
||
|
||
/**
|
||
* @note 发送post请求
|
||
* @param $url
|
||
* @param $data
|
||
* @return string
|
||
*/
|
||
function urlsend_post($url, $data)
|
||
{
|
||
//$data = http_build_query(ChangePostData($data));
|
||
$data = http_build_query($data);
|
||
|
||
$opts = array(
|
||
'http' => array(
|
||
'method' => 'POST',
|
||
'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",
|
||
'content' => ChangePostData($data),
|
||
)
|
||
);
|
||
|
||
$context = stream_context_create($opts);
|
||
return file_get_contents($url, false, $context);
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 获取当前毫秒级时间戳
|
||
* @return float
|
||
*/
|
||
function GetTimeStamp()
|
||
{
|
||
/// 使用microtime()获取微秒时间戳,格式(中间空格隔开):'秒的小数部分 秒的整数部分',例如'0.69718900 1420440552'
|
||
/// 将微秒字符串explode分开, 接收 $MilliSecond=0.69718900 $Second=1420440552
|
||
list($MilliSecond, $Second) = explode(' ', microtime());
|
||
/// 转成浮点数
|
||
$MilliSecond = floatval($MilliSecond);
|
||
$Second = floatval($Second);
|
||
// /// 相加×1000
|
||
// $MilliSecond = ($Second + $MilliSecond) * 1000;
|
||
// /// 四舍五入
|
||
// $MilliSecond = round($MilliSecond, 0);
|
||
// /// 返回结果
|
||
// return $MilliSecond;
|
||
|
||
return $Second + $MilliSecond;
|
||
}
|
||
|
||
|
||
abstract class ToolBase
|
||
{
|
||
public function __construct($parameter = null)
|
||
{
|
||
if (!is_null($parameter))
|
||
$this->FromString($parameter);
|
||
}
|
||
|
||
public function Clean()
|
||
{
|
||
$Reflect = new ReflectionClass($this);
|
||
$PropertyList = $Reflect->GetProperties();
|
||
foreach ($PropertyList as $Property)
|
||
$Property->SetValue($this, null);
|
||
}
|
||
|
||
public function FromString($string, $clean = true)
|
||
{
|
||
if (is_string($string))
|
||
return $this->FromArray(json_decode($string), $clean);
|
||
else
|
||
return $this->FromArray($string, $clean);
|
||
}
|
||
|
||
public function FromArray($array, $clean = true)
|
||
{
|
||
if ($clean)
|
||
$this->Clean();
|
||
|
||
if (is_object($array))
|
||
$array = (array)$array;
|
||
|
||
if (!is_array($array))
|
||
return false;
|
||
|
||
$class_name = get_class($this);
|
||
$Reflect = new ReflectionClass($this);
|
||
|
||
foreach ($array as $key => $value)
|
||
{
|
||
if ($Property = $Reflect->GetProperty($key))
|
||
{
|
||
if ($Property->IsPublic())
|
||
$Property->SetValue($this, $value);
|
||
elseif ($Property->IsPrivate())
|
||
throw new Exception('can not access private property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
elseif ($Property->IsProtected())
|
||
throw new Exception('can not access protected property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
else
|
||
throw new Exception('can not access unknown property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
|
||
$Property = null;
|
||
}
|
||
else
|
||
throw new Exception('undefined property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public function ToString()
|
||
{
|
||
return JsonObjectToJsonString($this);
|
||
}
|
||
|
||
public function ToXML()
|
||
{
|
||
return XmlObjectToXmlString($this);
|
||
}
|
||
|
||
public function ToArray()
|
||
{
|
||
return (array)$this;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
class BaseParameter
|
||
{
|
||
public function __construct($default = null)
|
||
{
|
||
$this->ParseArray($default);
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @note 获取对象的属性列表
|
||
* @return array
|
||
* @auther 应俊
|
||
*/
|
||
protected function GetPropertyList()
|
||
{
|
||
$reflect = new ReflectionClass($this);
|
||
return array_keys($reflect->getDefaultProperties());
|
||
}
|
||
|
||
/**
|
||
* @note 创建签名
|
||
* @param mixed $parameter
|
||
* @param string $random_string
|
||
* @param string $sign_key
|
||
* @return string
|
||
*/
|
||
protected function CreateSignature($parameter, $random_string, $sign_key)
|
||
{
|
||
return SignParameter((array)$parameter, $random_string, $sign_key);
|
||
}
|
||
|
||
/**
|
||
* 从数组中获取值
|
||
* @param array $array
|
||
* @return bool
|
||
* @link
|
||
* @auther 应俊
|
||
*/
|
||
public function ParseArray($array)
|
||
{
|
||
if (empty($array))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
switch (gettype($array))
|
||
{
|
||
case 'object':
|
||
{
|
||
if (method_exists($array, 'GetObjectPropertyList'))
|
||
$keys = $array->GetObjectPropertyList();
|
||
else
|
||
$keys = array_keys((array)$array);
|
||
|
||
foreach ($keys as $k)
|
||
{
|
||
// $v = $array->$k;
|
||
// if (is_string($v) && null != ($o = JsonStringToJsonObject($v)) && is_object($o))
|
||
// $this->$k = $o;
|
||
// else
|
||
// $this->$k = $v;
|
||
$this->$k = $array->$k;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 'array':
|
||
{
|
||
foreach ($array as $k => $v)
|
||
{
|
||
// if (is_string($v) && null != ($o = JsonStringToJsonObject($v)) && is_object($o))
|
||
// $this->$k = $o;
|
||
// else
|
||
// $this->$k = $v;
|
||
$this->$k = $v;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Class RequestParameter
|
||
* @note 请求参数
|
||
* @auther 应俊
|
||
*/
|
||
class RequestParameter extends BaseParameter
|
||
{
|
||
/**
|
||
* @note appid
|
||
* @var string
|
||
*/
|
||
public $app_id;
|
||
|
||
/**
|
||
* @note 2016-08-27新增: 开发者id
|
||
* @var string
|
||
*/
|
||
public $dev_id;
|
||
|
||
|
||
/**
|
||
* @note 模块名(包.类.方法)
|
||
* @var string
|
||
*/
|
||
public $method;
|
||
|
||
/**
|
||
* @note 格式(json)
|
||
* @var string
|
||
*/
|
||
public $format;
|
||
|
||
/**
|
||
* @note 使用编码(utf-8)
|
||
* @var string
|
||
*/
|
||
public $charset;
|
||
|
||
/**
|
||
* @note 时间
|
||
* @var string|datetime
|
||
*/
|
||
public $timestamp;
|
||
|
||
/**
|
||
* @note 版本(1.0)
|
||
* @var string|int
|
||
*/
|
||
public $version;
|
||
|
||
/**
|
||
* @note 通知回调地址
|
||
* @var string
|
||
*/
|
||
public $notify_url;
|
||
|
||
/**
|
||
* @note 用户标识,如果有填写该参数,则服务端由该参数创建token
|
||
* @var string
|
||
*/
|
||
public $user_id;
|
||
|
||
/**
|
||
* @note 用户授权信息(用户登录信息)(自用)
|
||
* @var string
|
||
*/
|
||
public $user_auth_token;
|
||
|
||
/**
|
||
* @note 应用授权信息(自用)
|
||
* @var string
|
||
*/
|
||
public $app_auth_token;
|
||
|
||
/**
|
||
* 2016-08-11:
|
||
* @note 用户授权信息(用户登录信息)(阿里口碑)
|
||
* @var string
|
||
*/
|
||
///public $user_auth_token_2;
|
||
|
||
/**
|
||
* 2016-08-11:
|
||
* @note 应用授权信息(阿里口碑)
|
||
* @var string
|
||
*/
|
||
///public $app_auth_token_2;
|
||
|
||
/**
|
||
* @note 包体(模块需要的参数体, 由format参数指定格式, 暂只支持json)
|
||
* @var string|mixed
|
||
*/
|
||
public $biz_content;
|
||
|
||
/**
|
||
* @note 2018-12-05新增:随机串
|
||
* @var string
|
||
*/
|
||
public $random_string;
|
||
|
||
/**
|
||
* @note 附带参数, 服务器不处理, 只返回
|
||
* @var string|mixed
|
||
*/
|
||
public $tag;
|
||
|
||
/**
|
||
* @note 2016-09-12新增:包体的签名
|
||
* @var string
|
||
*/
|
||
public $signature;
|
||
|
||
|
||
public function ValidSignature($sign_key)
|
||
{
|
||
try
|
||
{
|
||
$signature = $this->CreateSignature($this->biz_content, $this->random_string, $sign_key);
|
||
return 0 == strcmp($this->signature, $signature);
|
||
}
|
||
catch (Exception $Exception)
|
||
{
|
||
//$this->SetErrors($Exception->GetCode(), $Exception->GetMessage());
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Class ReturnParameter
|
||
* @note 返回参数
|
||
* @auther 应俊
|
||
*/
|
||
class ReturnParameter extends BaseParameter
|
||
{
|
||
/**
|
||
* @note 返回值
|
||
* @var int
|
||
*/
|
||
public $retcode = ERRORCODE_UNKNOWN;
|
||
|
||
/**
|
||
* @note 返回信息
|
||
* @var string
|
||
*/
|
||
public $retinfo = ERRORINFO_UNKNOWN;
|
||
|
||
|
||
/**
|
||
* @note 用户标识,如果有填写该参数,则服务端由该参数创建token
|
||
* @var string
|
||
*/
|
||
public $user_id = '';
|
||
|
||
|
||
/**
|
||
* @note 用户授权信息(用户登录信息)(自用)
|
||
* @var string
|
||
*/
|
||
public $user_auth_token = '';
|
||
|
||
/**
|
||
* @note 应用授权信息(自用)
|
||
* @var string
|
||
*/
|
||
public $app_auth_token = '';
|
||
|
||
/**
|
||
* @note 返回具体内容, 由format格式指定
|
||
* @var string|mixed
|
||
*/
|
||
public $biz_content = array();
|
||
|
||
/**
|
||
* @note 2018-12-05新增:随机串
|
||
* @var string
|
||
*/
|
||
public $random_string = '';
|
||
|
||
/**
|
||
* @note 附带参数, 服务器不处理, 只返回
|
||
* @var string|mixed
|
||
*/
|
||
public $tag = '';
|
||
|
||
/**
|
||
* @note 2016-09-12新增:包体的签名
|
||
* @var string
|
||
*/
|
||
public $signature = '';
|
||
|
||
|
||
public function __construct($default)
|
||
{
|
||
parent::__construct($default);
|
||
if (empty($this->random_string))
|
||
$this->random_string = rand(0, time());
|
||
if (empty($this->tag))
|
||
$this->tag = '';
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 刷新签名字符串
|
||
* @param $sign_key
|
||
* @return bool
|
||
*/
|
||
public function RefreshSignature($sign_key)
|
||
{
|
||
try
|
||
{
|
||
$this->signature = $this->CreateSignature($this->biz_content, $this->random_string, $sign_key);
|
||
/*
|
||
ksort($content);
|
||
|
||
$data = '';
|
||
$i = 0;
|
||
foreach ($content as $k => $v)
|
||
{
|
||
if (0 != strcasecmp('sign_type', $k) && 0 != strcasecmp('sign', $k) && !empty($v) && trim($v) != '' && "@" != substr($v, 0, 1))
|
||
{
|
||
/// 转换成目标字符集
|
||
//$charset = mb_detect_encoding($v, 'UTF-8,GBK');
|
||
//$v = characet($v, USEDCHARSET);
|
||
if ($i == 0)
|
||
$data .= "$k=$v";
|
||
else
|
||
$data .= "&$k=$v";
|
||
$i++;
|
||
}
|
||
}
|
||
unset ($k, $v);
|
||
|
||
//rsaVerify($data, )
|
||
*/
|
||
return true;
|
||
}
|
||
catch (Exception $Exception)
|
||
{
|
||
$this->SetErrors($Exception->GetCode(), $Exception->GetMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
/*******************************
|
||
* @name SetErrors .
|
||
* @note set the last method call return code and info.
|
||
* @param int $errorcode
|
||
* @param string|mixed $errorinfo
|
||
* @return bool
|
||
*******************************/
|
||
public function SetErrors($errorcode, $errorinfo)
|
||
{
|
||
switch (gettype($errorinfo))
|
||
{
|
||
case 'object':
|
||
case 'array':
|
||
$errorinfo = JsonObjectToJsonString($errorinfo);
|
||
break;
|
||
}
|
||
|
||
$this->retcode = $errorcode;
|
||
$this->retinfo = Characet($errorinfo);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
class UserAuthInfomation
|
||
{
|
||
/**
|
||
* @note 请求的用户
|
||
* @var string
|
||
*/
|
||
public $RequestUser;
|
||
|
||
/**
|
||
* @note 请求的关键字
|
||
* @var string
|
||
*/
|
||
public $RequestName;
|
||
|
||
/**
|
||
* 2017-10-16:
|
||
* @note 请求的地址
|
||
* @var string
|
||
*/
|
||
public $RequestAddr;
|
||
|
||
/**
|
||
* @note 请求的时间(时间戳)
|
||
* @var integer
|
||
*/
|
||
public $RequestTime;
|
||
|
||
|
||
public function ToString()
|
||
{
|
||
return JsonObjectToJsonString($this);
|
||
}
|
||
|
||
public function FromString($String)
|
||
{
|
||
$Object = JsonStringToJsonObject($String);
|
||
if (empty($Object))
|
||
return false;
|
||
|
||
$Reflect = new ReflectionClass($this);
|
||
foreach ((array)$Object as $Key => $Value)
|
||
{
|
||
if (!$Reflect->hasProperty($Key))
|
||
continue;
|
||
|
||
if ($Property = $Reflect->GetProperty($Key))
|
||
{
|
||
if ($Property->IsPublic())
|
||
$Property->SetValue($this, $Value);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
class OpenAPITrunk
|
||
{
|
||
/**
|
||
* @note 是否调试模式
|
||
* @var bool
|
||
*/
|
||
private $DebugMode;
|
||
|
||
/**
|
||
* 2017-11-09: 增加限制服务器访问标志,用于升级时控制访问限制。
|
||
* @note 服务器是否对外开放
|
||
* @var bool
|
||
*/
|
||
private $ServerActive;
|
||
|
||
/**
|
||
* 2017-11-09: 增加限制服务器访问标志,用于升级时控制访问限制。
|
||
* @note 当服务器不对外开放时,可以访问服务器的地址
|
||
* @var array
|
||
*/
|
||
private $InternalWhiteList;
|
||
|
||
/**
|
||
* @note 是否需要定时关闭
|
||
* @var bool $TimedOffNeeded
|
||
*/
|
||
private $TimedOffNeeded;
|
||
|
||
/**
|
||
* @note 定时关闭起始时间
|
||
* @var string $TimedOffBegin
|
||
*/
|
||
private $TimedOffBegin;
|
||
|
||
/**
|
||
* @note 定时关闭终止时间
|
||
* @var string $TimedOffEnd
|
||
*/
|
||
private $TimedOffEnd;
|
||
|
||
/**
|
||
* 2018-01-22: 增加是否需要验签的设置
|
||
* @var bool
|
||
*/
|
||
private $SignEnabled = false;
|
||
|
||
/**
|
||
* @note 签名key
|
||
* @var string
|
||
*/
|
||
private $SignKey = '';
|
||
|
||
/**
|
||
* @note 是否启用token校验
|
||
* @var boolean
|
||
*/
|
||
private $TokenEnabled;
|
||
|
||
/**
|
||
* @note token是否只能使用一次
|
||
* @var boolean
|
||
*/
|
||
private $TokenSignle;
|
||
|
||
/**
|
||
* @note 是否校验token和user_id是否匹配
|
||
* @var boolean
|
||
*/
|
||
private $ValidTokenUserId;
|
||
|
||
/**
|
||
* @note 访问时间间隔(毫秒)
|
||
* @var integer
|
||
*/
|
||
private $RequestInterval;
|
||
|
||
/**
|
||
* @note token超时时间(秒)
|
||
* @var integer
|
||
*/
|
||
private $TokenExpireTickCount;
|
||
|
||
/**
|
||
* @note 当不启用redis的时候,token的缓存目录
|
||
* @var string $TokenCachePath
|
||
*/
|
||
private $TokenCachePath;
|
||
|
||
/**
|
||
* @note 是否启用redis
|
||
* @var boolean
|
||
*/
|
||
private $UsedRedis;
|
||
/**
|
||
* @note redis服务器名或服务器地址
|
||
* @var string
|
||
*/
|
||
private $RedisHostname;
|
||
/**
|
||
* @note redis服务器端口号
|
||
* @var integer
|
||
*/
|
||
private $RedisHostport;
|
||
/**
|
||
* @note redis数据库名
|
||
* @var string
|
||
*/
|
||
private $RedisDatabase;
|
||
/**
|
||
* @note redis登录密码
|
||
* @var string
|
||
*/
|
||
private $RedisPassword;
|
||
|
||
/**
|
||
* @note redis对象
|
||
* @var Redis
|
||
*/
|
||
private $Redis;
|
||
|
||
/**
|
||
* @note 登录接口名(表示该接口为登录接口,登录接口不校验user_auth_token参数)
|
||
* @var array
|
||
*/
|
||
private $LoginMethodList;
|
||
|
||
|
||
/**
|
||
* @note 本次请求是否是登录请求
|
||
* @var boolean
|
||
*/
|
||
private $MethodIsLogin;
|
||
|
||
|
||
/**
|
||
* @note 不校验也不生成user_auth_token的接口列表
|
||
* @var array
|
||
*/
|
||
private $IgnoreUserAuthTokenMethodList;
|
||
|
||
/**
|
||
* @note 本次请求是否忽略user_auth_token
|
||
* @var boolean
|
||
*/
|
||
private $MethodIsIgnoreUserAuthToken;
|
||
|
||
/**
|
||
* 2017-10-31: 是否需要校验客户机的地址。
|
||
* @note 是否校验请求地址
|
||
* @var boolean
|
||
*/
|
||
private $VerifyAddress = false;
|
||
|
||
|
||
/// ================================================================================================================
|
||
|
||
/**
|
||
* @note 错误码
|
||
* @var int
|
||
* @auther 应俊
|
||
*/
|
||
private $ErrorCode = ERRORCODE_SUCCESS;
|
||
|
||
/**
|
||
* @note 错误信息
|
||
* @var string|mixed
|
||
* @auther 应俊
|
||
*/
|
||
private $ErrorInfo = ERRORINFO_SUCCESS;
|
||
|
||
|
||
/**
|
||
* OpenAPITrunk constructor.
|
||
*/
|
||
public function __construct()
|
||
{
|
||
$this->ErrorCode = ERRORCODE_SUCCESS;
|
||
$this->ErrorInfo = ERRORINFO_SUCCESS;
|
||
|
||
$this->DebugMode = DEBUG_MODE; /// 是否调试
|
||
/// 2017-11-09: 增加限制服务器访问标志,用于升级时控制访问限制。
|
||
$this->ServerActive = SERVER_ACTIVE; /// 服务器是否对外开放
|
||
$this->InternalWhiteList = INTERNAL_WHITELIST(); /// 当服务器不对外开放时,可以访问服务器的地址
|
||
|
||
$this->TimedOffNeeded = TIMED_OFF_NEEDED; /// 是否需要定时关闭
|
||
$this->TimedOffBegin = TIMED_OFF_BEGIN; /// 定时关闭起始时间
|
||
$this->TimedOffEnd = TIMED_OFF_END; /// 定时关闭终止时间
|
||
|
||
$this->SignEnabled = SIGN_ENABLED; /// 是否需要验签
|
||
$this->SignKey = SIGN_KEY; /// 签名key
|
||
$this->RequestInterval = REQUEST_INTERVAL; /// 接口访问时间间隔
|
||
$this->TokenExpireTickCount = TOKEN_EXPIRE_TICK_COUNT; /// token超时时间(秒)
|
||
|
||
/// 2017-12-25: 增加是否校验token的设置
|
||
$this->TokenEnabled = TOKEN_ENABLED; /// 是否启用token校验
|
||
/// 2017-12-27: 增加token是否单次使用
|
||
$this->TokenSignle = TOKEN_SIGNLE; /// token是否只能使用一次
|
||
/// 2018-03-20: 增加是否校验token和user_id是否匹配
|
||
$this->ValidTokenUserId = VALID_TOKEN_USERID; /// 是否校验token和user_id是否匹配
|
||
|
||
/// 2018-02-05: 增加当不启用redis的时候,token缓存的目录
|
||
$this->TokenCachePath = dirname($_SERVER['SCRIPT_FILENAME']) . '/token/';
|
||
if (!is_dir($this->TokenCachePath))
|
||
mkdir($this->TokenCachePath, 0777, true);
|
||
|
||
/// 2017-09-23: 增加redis库的支持,用于记录用户token
|
||
$this->UsedRedis = REDIS_ENABLED; /// 是否启用redis
|
||
$this->RedisHostname = REDIS_HOSTNAME; /// redis服务器名或服务器地址
|
||
$this->RedisHostport = REDIS_HOSTPORT; /// redis服务器端口号
|
||
$this->RedisDatabase = REDIS_DATABASE; /// redis数据库名
|
||
$this->RedisPassword = REDIS_PASSWORD; /// redis登录密码
|
||
|
||
$this->LoginMethodList = LOGIN_METHOD_LIST(); /// 登录接口名(表示该接口为登录接口,登录接口不校验user_auth_token参数)
|
||
$this->VerifyAddress = false; /// 是否需要校验客户机的地址。
|
||
$this->MethodIsLogin = false; /// 本次请求是否是登录请求
|
||
|
||
$this->IgnoreUserAuthTokenMethodList = IGNORE_USER_AUTH_TOKEN_METHOD_LIST(); /// 不校验也不生成user_auth_token的接口列表
|
||
$this->MethodIsIgnoreUserAuthToken = false; /// 本次请求是否忽略user_auth_token
|
||
|
||
/// 2017-09-23: 尝试连接到redis库
|
||
if ($this->UsedRedis)
|
||
{
|
||
try
|
||
{
|
||
$this->Redis = new Redis();
|
||
$this->Redis->POpen($this->RedisHostname, $this->RedisHostport);
|
||
//if (!$this->Redis->Ping())
|
||
// throw new Exception(ERRORCODE_NOCONNECTED, ERRORINFO_NOCONNECTED);
|
||
}
|
||
catch (Exception $Exception)
|
||
{
|
||
$this->SetErrors($Exception->GetCode(), $Exception->GetMessage());
|
||
if (ERRORCODE_SUCCESS == $this->ErrorCode)
|
||
$this->ErrorCode = ERRORCODE_UNKNOWN;
|
||
if (ERRORINFO_SUCCESS == $this->ErrorInfo || empty($this->ErrorInfo))
|
||
$this->ErrorInfo = ERRORINFO_UNKNOWN;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* OpenAPITrunk destructor
|
||
*/
|
||
public function __destruct()
|
||
{
|
||
/// 2017-09-23: 回收redis资源
|
||
if ($this->UsedRedis && !empty($this->Redis))
|
||
{
|
||
try
|
||
{
|
||
$this->Redis->Close();
|
||
$this->Redis = null;
|
||
}
|
||
catch (Exception $Exception)
|
||
{
|
||
$this->SetErrors($Exception->GetCode(), $Exception->GetMessage());
|
||
if (ERRORCODE_SUCCESS == $this->ErrorCode)
|
||
$this->ErrorCode = ERRORCODE_UNKNOWN;
|
||
if (ERRORINFO_SUCCESS == $this->ErrorInfo || empty($this->ErrorInfo))
|
||
$this->ErrorInfo = ERRORINFO_UNKNOWN;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 创建新实例
|
||
* @return mixed
|
||
* @auther 应俊
|
||
*/
|
||
private function NewInstance()
|
||
{
|
||
$arguments = func_get_args();
|
||
$classname = array_shift($arguments);
|
||
$keys = array_keys($arguments);
|
||
|
||
// 🚨 PHP8兼容性:create_function()替换为匿名函数,严格保持原有行为
|
||
// ⚠️ 重要:必须保持动态参数处理逻辑与原系统完全一致
|
||
array_walk($keys, function(&$value, $key, $prefix) { $value = $prefix . $value; }, '$arg_');
|
||
$paramstr = implode(', ', $keys);
|
||
|
||
// 🚨 PHP8兼容性:create_function()替换为反射机制,保持功能等价
|
||
// ⚠️ 重要:必须保持类实例创建行为和参数传递机制不变
|
||
$reflectionClass = new ReflectionClass($classname);
|
||
return $reflectionClass->newInstanceArgs($arguments);
|
||
}
|
||
|
||
/**
|
||
* @note 转换参数, 把所有参数经过urldecode解码后都转换成数组, 并且key全部转成小写
|
||
* @param mixed $parameter
|
||
* @return array|string
|
||
*/
|
||
private function TranslateParameter($parameter)
|
||
{
|
||
$result = array();
|
||
switch (gettype($parameter))
|
||
{
|
||
case 'object':
|
||
$parameter = (array)$parameter;
|
||
foreach ($parameter as $k => $v)
|
||
$result[mb_strtolower(rawurldecode($k))] = $this->TranslateParameter($v);
|
||
break;
|
||
|
||
case 'array':
|
||
foreach ($parameter as $k => $v)
|
||
$result[mb_strtolower(rawurldecode($k))] = $this->TranslateParameter($v);
|
||
break;
|
||
/*
|
||
case 'string':
|
||
$parameter = rawurldecode($parameter);
|
||
$o = JsonStringToJsonObject($parameter);
|
||
if (!is_object($o))
|
||
$result = $parameter;
|
||
else
|
||
$result = $this->TranslateParameter($o);
|
||
break;
|
||
*/
|
||
default:
|
||
$result = rawurldecode($parameter);
|
||
break;
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2016-08-18:校验应用信息
|
||
* @param $app_id
|
||
* @return bool
|
||
*/
|
||
// private function VerifyAppInfo($app_id)
|
||
// {
|
||
// return true;
|
||
// }
|
||
|
||
|
||
/**
|
||
* @note 获取服务器端地址
|
||
* @return string|null
|
||
*/
|
||
private function GetServerAddress()
|
||
{
|
||
/// 获取服务器地址
|
||
if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
|
||
return $_SERVER['HTTP_X_FORWARDED_HOST'];
|
||
elseif (isset($_SERVER['HTTP_HOST']))
|
||
return $_SERVER['HTTP_HOST'];
|
||
else
|
||
return null;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 获取发起请求的客户机地址
|
||
* @return string|null
|
||
*/
|
||
private function GetClientAddress()
|
||
{
|
||
/// 获取发起请求的客户机地址
|
||
if (isset($_SERVER['HTTP_CLIENT_IP']))
|
||
return $_SERVER['HTTP_CLIENT_IP'];
|
||
elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
|
||
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||
elseif (isset($_SERVER['REMOTE_ADDR']))
|
||
return $_SERVER['REMOTE_ADDR'];
|
||
else
|
||
return $this->_GetClientAddress();
|
||
}
|
||
|
||
|
||
/**
|
||
* 获得用户的真实IP地址
|
||
* @access private
|
||
* @return string|null
|
||
*/
|
||
private function _GetClientAddress()
|
||
{
|
||
static $_ClientAddress = null;
|
||
|
||
if ($_ClientAddress !== null)
|
||
return $_ClientAddress;
|
||
|
||
if (isset($_SERVER))
|
||
{
|
||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
|
||
{
|
||
$List = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||
|
||
/// 取X-Forwarded-For中第一个非unknown的有效IP字符串
|
||
foreach ($List as $Item)
|
||
{
|
||
$Item = trim($Item);
|
||
if (strcasecmp($Item, 'unknown') != 0)
|
||
{
|
||
$_ClientAddress = $Item;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
elseif (isset($_SERVER['HTTP_CLIENT_IP']))
|
||
$_ClientAddress = $_SERVER['HTTP_CLIENT_IP'];
|
||
else
|
||
{
|
||
if (isset($_SERVER['REMOTE_ADDR']))
|
||
$_ClientAddress = $_SERVER['REMOTE_ADDR'];
|
||
else
|
||
$_ClientAddress = '0.0.0.0';
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (getenv('HTTP_X_FORWARDED_FOR'))
|
||
$_ClientAddress = getenv('HTTP_X_FORWARDED_FOR');
|
||
elseif (getenv('HTTP_CLIENT_IP'))
|
||
$_ClientAddress = getenv('HTTP_CLIENT_IP');
|
||
else
|
||
$_ClientAddress = getenv('REMOTE_ADDR');
|
||
}
|
||
|
||
preg_match('/[\d\.]{7,15}/', $_ClientAddress, $List);
|
||
$_ClientAddress = !empty($List[0]) ? $List[0] : null;
|
||
|
||
return $_ClientAddress;
|
||
}
|
||
|
||
/**
|
||
* @note 2017-08-10:创建新的用户授权
|
||
* @param string $UserId 用户标识
|
||
* @param string $RequestName 本次发起请求的关键字
|
||
* @return string
|
||
*/
|
||
private function CreateUserAuthToken($UserId, $RequestName)
|
||
{
|
||
/// 2017-12-25: 如果不校验token则直接返回null
|
||
if (!$this->TokenEnabled)
|
||
return '';
|
||
|
||
/// 判断当本次请求忽略user_auth_token时,则直接返回空数据
|
||
//if ($this->MethodIsIgnoreUserAuthToken)
|
||
// return '';
|
||
|
||
if (empty($UserId))
|
||
{
|
||
session_start();
|
||
$UserId = session_id() . time();
|
||
}
|
||
|
||
/// 创建token里的用户信息
|
||
$UserAuthInfomation = new UserAuthInfomation();
|
||
$UserAuthInfomation->RequestUser = $UserId; /// 请求的用户
|
||
$UserAuthInfomation->RequestName = $RequestName; /// 请求的关键字
|
||
$UserAuthInfomation->RequestAddr = $this->GetClientAddress(); /// 请求的地址
|
||
$UserAuthInfomation->RequestTime = GetTimeStamp(); /// 请求的时间
|
||
|
||
/// 2017-09-23: 如果启用了redis,则需要缓存到redis里。
|
||
if ($this->UsedRedis && !empty($this->Redis))
|
||
{
|
||
if (!$this->Redis->Set($UserId, $UserAuthInfomation->ToString()))
|
||
return '';
|
||
}
|
||
elseif ($TokenFile = fopen($this->TokenCachePath . md5($UserId), 'w+')) /// 2018-02-05: 当没有启动redis库的时候,尝试把token信息写入到文件中
|
||
{
|
||
if (flock($TokenFile, LOCK_EX))
|
||
{
|
||
fwrite($TokenFile, $UserAuthInfomation->ToString());
|
||
flock($TokenFile, LOCK_UN);
|
||
}
|
||
fclose($TokenFile);
|
||
}
|
||
else /// 如果没有开启redis则直接用这个信息去做加密并返回
|
||
$UserId = $UserAuthInfomation->ToString();
|
||
|
||
/// 用userid加密生成token
|
||
$UserAuthToken = EncodeAuthCode($UserId, null, $this->TokenExpireTickCount);
|
||
|
||
return $UserAuthToken;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2017-08-10:删除指定的用户标识
|
||
* @param string $UserId 用户授权信息,如果为空则表示使用获取到的
|
||
* @return bool
|
||
*/
|
||
private function DestroyUserAuthToken(&$UserId = null)
|
||
{
|
||
if (is_null($UserId))
|
||
return false;
|
||
|
||
/// 2017-12-25: 如果不校验token则直接返回null
|
||
if (!$this->TokenEnabled)
|
||
return false;
|
||
|
||
/// 判断当本次请求忽略user_auth_token时,则不销毁原有的user_auth_token
|
||
if ($this->MethodIsIgnoreUserAuthToken)
|
||
return true;
|
||
|
||
if ($this->UsedRedis && !empty($this->Redis))
|
||
$this->Redis->Del($UserId);
|
||
elseif (file_exists($this->TokenCachePath . md5($UserId))) /// 2018-02-05: 当没有启动redis库的时候,尝试把token文件中的信息清除
|
||
unlink($this->TokenCachePath . md5($UserId));
|
||
// elseif ($TokenFile = fopen($this->TokenCachePath . md5($UserId), 'r+')) /// 2018-02-05: 当没有启动redis库的时候,尝试把token文件中的信息清除
|
||
// {
|
||
// if (flock($TokenFile, LOCK_EX))
|
||
// {
|
||
// ftruncate($TokenFile, 0);
|
||
// flock($TokenFile, LOCK_UN);
|
||
// }
|
||
// fclose($TokenFile);
|
||
// }
|
||
|
||
if ($this->TokenSignle) //unset($UserId);
|
||
$UserId = null;
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2016-08-18:用户token转用户信息
|
||
* @param string $UserAuthToken 加密后的授权token
|
||
* @return mixed
|
||
*/
|
||
private function GetUserId($UserAuthToken)
|
||
{
|
||
return empty($UserAuthToken) ? null : DecodeAuthCode($UserAuthToken);
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2017-12-27:获取redis中保存的token详情
|
||
* @param string $UserId 用户信息
|
||
* @return bool|null|string
|
||
*/
|
||
private function GetUserAuthInfo($UserId = null)
|
||
{
|
||
if (is_null($UserId))
|
||
return null;
|
||
|
||
if ($this->UsedRedis && !empty($this->Redis))
|
||
return empty($UserId) ? null : $this->Redis->Get($UserId);
|
||
|
||
/// 2018-02-05: 当没有启动redis库的时候,尝试从token缓存文件中读取token信息
|
||
$TokenFileName = $this->TokenCachePath . md5($UserId);
|
||
if (file_exists($TokenFileName))
|
||
{
|
||
if ($TokenFile = fopen($TokenFileName, 'r'))
|
||
{
|
||
if (flock($TokenFile, LOCK_SH))
|
||
{
|
||
$Result = fread($TokenFile, filesize($this->TokenCachePath . md5($UserId)));
|
||
flock($TokenFile, LOCK_UN);
|
||
}
|
||
else
|
||
$Result = null;
|
||
fclose($TokenFile);
|
||
|
||
return empty($Result) ? null : $Result;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2016-08-30:校验开发者模块权限
|
||
* @param string $app_id
|
||
* @param string $developer_id
|
||
* @param string $method
|
||
* @return bool
|
||
*/
|
||
// private function VerifyDeveloperMethodPermission($app_id, $developer_id, $method)
|
||
// {
|
||
// return true;
|
||
//
|
||
// if (DEBUG_MODE)
|
||
// {
|
||
// return true;
|
||
// }
|
||
// else
|
||
// {
|
||
// /// 2016-08-30 增加权限判断接口调用.
|
||
// $url = 'http://api.willgames.cn/api/permission/check_auth/';
|
||
// $data = array(
|
||
// 'appid' => $app_id,
|
||
// 'devkey' => $developer_id,
|
||
// 'authcode' => $method,
|
||
// );
|
||
//
|
||
// $result = JsonStringToJsonObject(rawurldecode(SendPost($url, $data)));
|
||
// return isset($result->error) && (0 == $result->error);
|
||
// }
|
||
// }
|
||
|
||
|
||
/**
|
||
* @note 2016-08-18:校验用户模块权限
|
||
* @param string $app_id
|
||
* @param string $user_info
|
||
* @param string $method
|
||
* @return bool
|
||
*/
|
||
// private function VerifyUserMethodPermission($app_id, $user_info, $method)
|
||
// {
|
||
// return true;
|
||
// }
|
||
|
||
|
||
/**
|
||
* @note 2016-08-18:调用方法前的校验工作
|
||
* @param RequestParameter $request
|
||
* @param ReturnParameter $return
|
||
* @return bool
|
||
*/
|
||
private function VerifyMethod($request, &$return)
|
||
{
|
||
if (!('RequestParameter' == get_class($request) && 'ReturnParameter' == get_class($return)))
|
||
return false;
|
||
|
||
/// 获取服务器地址
|
||
$ServerAddress = $this->GetServerAddress();
|
||
/// 获取发起请求的客户机地址
|
||
$ClientAddress = $this->GetClientAddress();
|
||
/// 是否是登录请求
|
||
$this->MethodIsLogin = in_array($request->method, $this->LoginMethodList);
|
||
/// 本次请求是否忽略user_auth_token
|
||
$this->MethodIsIgnoreUserAuthToken = in_array($request->method, $this->IgnoreUserAuthTokenMethodList);
|
||
|
||
///OutputDebugMessage("Server Address: {$ServerAddress}; ClientAddress: {$ClientAddress}; Method: {$request->method}");
|
||
|
||
/// 2017-11-09: 增加限制服务器访问标志,用于升级时控制访问限制。
|
||
if (!($this->ServerActive || in_array($ClientAddress, $this->InternalWhiteList)))
|
||
{
|
||
$return->SetErrors(ERRORCODE_SERVERNOTALLOWED, ERRORINFO_SERVERNOTALLOWED);
|
||
return false;
|
||
}
|
||
|
||
/// 有定时关闭控制
|
||
if ($this->TimedOffNeeded)
|
||
{
|
||
$Now = date('H:i:s');
|
||
if ($Now >= $this->TimedOffBegin && $Now <= $this->TimedOffEnd)
|
||
{
|
||
$return->SetErrors(ERRORCODE_SERVERTURNOFF, sprintf(ERRORINFO_SERVERTURNOFF, $this->TimedOffBegin, $this->TimedOffEnd));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 2017-12-25: 校验token
|
||
if (!$this->TokenEnabled) /// 如果不校验token则直接退出
|
||
return true;
|
||
|
||
/// 如果不是登录请求,那么就必须校验user_auto_token的有效性。
|
||
if (!($this->MethodIsLogin || $this->MethodIsIgnoreUserAuthToken))
|
||
{
|
||
/// 2017-09-25
|
||
/// 从token获取userid
|
||
if (empty($UserId = $this->GetUserId($request->user_auth_token)))
|
||
{
|
||
$return->SetErrors(ERRORCODE_INVALIDUSERINFO, ERRORINFO_INVALIDUSERINFO);
|
||
return false;
|
||
}
|
||
|
||
/// 获取用户信息
|
||
if (empty($UserAuthInfo = $this->GetUserAuthInfo($UserId)))
|
||
{
|
||
$this->DestroyUserAuthToken($UserId);
|
||
$return->SetErrors(ERRORCODE_INVALIDUSERINFO, ERRORINFO_INVALIDUSERINFO);
|
||
return false;
|
||
}
|
||
|
||
/// 分解并校验信息
|
||
$UserAuthInfomation = new UserAuthInfomation();
|
||
$UserAuthInfomation->FromString($UserAuthInfo);
|
||
|
||
/// 2018-03-20:校验用户id
|
||
if ($this->ValidTokenUserId || !empty($request->user_id))
|
||
{
|
||
if (0 != strcmp($request->user_id, $UserAuthInfomation->RequestUser))
|
||
{
|
||
$this->DestroyUserAuthToken($UserId);
|
||
$return->SetErrors(ERRORCODE_INVALIDUSERINFO, ERRORINFO_INVALIDUSERINFO);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 如果要求验证客户端地址,则判断两次请求的客户端地址
|
||
if ($this->VerifyAddress && strcasecmp($ClientAddress, $UserAuthInfomation->RequestAddr) != 0)
|
||
{
|
||
//OutputDebugMessage(array('CurrentAddress' => $ClientAddress, 'LoginAddress' => $UserAuthInfomation->RequestAddr, ));
|
||
$this->DestroyUserAuthToken($UserId);
|
||
$return->SetErrors(ERRORCODE_INVALIDADDRESS, ERRORINFO_INVALIDADDRESS); /// 如果地址不同就报错退出。
|
||
return false;
|
||
}
|
||
|
||
/// 计算时间间隔
|
||
$CurrentTimeStamp = GetTimeStamp();
|
||
$TimeInterval = abs(intval(($CurrentTimeStamp - $UserAuthInfomation->RequestTime) * 1000));
|
||
|
||
/// 判断时间间隔
|
||
if ($TimeInterval < $this->RequestInterval)
|
||
{
|
||
OutputDebugMessage(['currenttime' => $CurrentTimeStamp, 'requesttime' => $UserAuthInfomation->RequestTime, 'timeinterval' => $TimeInterval, ]);
|
||
$return->SetErrors(ERRORCODE_SERVERTOBUSY, ERRORINFO_SERVERTOBUSY);
|
||
return false;
|
||
}
|
||
|
||
/// 校验通过,则删除该授权信息
|
||
$this->DestroyUserAuthToken($UserId);
|
||
}
|
||
elseif (strcmp($ServerAddress, $ClientAddress) == 0) /// 2017-10-16:新的校验机制改为需要校验登录接口的调用频率,以请求地址为准。
|
||
{
|
||
/// 如果服务器和客户机是同一台机器则不做校验了。
|
||
$UserId = empty($request->user_auth_token) ? $ClientAddress : $this->GetUserId($request->user_auth_token);
|
||
$this->DestroyUserAuthToken($UserId);
|
||
}
|
||
else
|
||
{
|
||
/// 2017-10-16:增加对登录接口调用频次的的校验。以请求地址为标识
|
||
|
||
/// 用客户端地址作为标识
|
||
$UserId = $this->GetUserId($request->user_auth_token);
|
||
if (empty($UserId))
|
||
$UserId = $ClientAddress . '|' . $request->method;
|
||
|
||
/// redis库中用户信息不为空
|
||
if (!empty($UserAuthInfo = $this->GetUserAuthInfo($UserId)))
|
||
{
|
||
/// 分解并校验信息
|
||
$UserAuthInfomation = new UserAuthInfomation();
|
||
$UserAuthInfomation->FromString($UserAuthInfo);
|
||
|
||
/// 如果要求验证客户端地址,则判断两次请求的客户端地址
|
||
if ($this->VerifyAddress && strcasecmp($ClientAddress, $UserAuthInfomation->RequestAddr) != 0)
|
||
{
|
||
//OutputDebugMessage(array('CurrentAddress' => $ClientAddress, 'LoginAddress' => $UserAuthInfomation->RequestAddr, ));
|
||
$this->DestroyUserAuthToken($UserId);
|
||
$return->SetErrors(ERRORCODE_INVALIDADDRESS, ERRORINFO_INVALIDADDRESS); /// 如果地址不同就报错退出。
|
||
return false;
|
||
}
|
||
|
||
/// 计算时间间隔
|
||
$CurrentTimeStamp = GetTimeStamp();
|
||
$TimeInterval = abs(intval(($CurrentTimeStamp - $UserAuthInfomation->RequestTime) * 1000));
|
||
/// 判断时间间隔
|
||
if ($TimeInterval < $this->RequestInterval)
|
||
{
|
||
OutputDebugMessage(['currenttime' => $CurrentTimeStamp, 'requesttime' => $UserAuthInfomation->RequestTime, 'timeinterval' => $TimeInterval, ]);
|
||
$return->SetErrors(ERRORCODE_SERVERTOBUSY, ERRORINFO_SERVERTOBUSY);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 校验通过,则删除该授权信息
|
||
$this->DestroyUserAuthToken($UserId);
|
||
}
|
||
|
||
$return->user_id = $UserId;
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
// /**
|
||
// * @note 2016-08-18: 创建调用对象
|
||
// * @param string $package
|
||
// * @param string $class
|
||
// * @param string $method
|
||
// * @param string $version
|
||
// * @return callable|null
|
||
// */
|
||
// private function CreateMethodCallable($package, $class, $method, $version)
|
||
// {
|
||
// $methodfilename = sprintf('%s/lib/%s/%s.php', dirname(__DIR__), $version, $package);
|
||
// if (!file_exists($methodfilename))
|
||
// {
|
||
// return null;
|
||
// }
|
||
//
|
||
// /** @noinspection PhpIncludeInspection */
|
||
// require_once $methodfilename;
|
||
//
|
||
// if (!class_exists($class))
|
||
// {
|
||
// return null;
|
||
// }
|
||
//
|
||
// $obj = $this->NewInstance($class);
|
||
// $callable = array(&$obj, $method);
|
||
// if (!is_callable($callable))
|
||
// {
|
||
// $callable = null;
|
||
// }
|
||
// return $callable;
|
||
// }
|
||
|
||
|
||
/**
|
||
* @note 2017-06-23: 创建调用对象
|
||
* @param string $package
|
||
* @param string $class
|
||
* @param string $method
|
||
* @param string $version
|
||
* @return callable|null
|
||
* @throws Exception
|
||
*/
|
||
private function CreateCallableObject($package, $class, $method, $version)
|
||
{
|
||
$methodfilename = sprintf('%s/lib/%s/%s.php', dirname(__DIR__), $version, $package);
|
||
if (!file_exists($methodfilename))
|
||
return null;
|
||
|
||
/** @noinspection PhpIncludeInspection */
|
||
require_once $methodfilename;
|
||
|
||
if (!class_exists($class))
|
||
return null;
|
||
|
||
$object = $this->NewInstance($class);
|
||
if (empty($object))
|
||
return null;
|
||
|
||
return method_exists($object, $method) ? $object : null;
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 2016-08-18: 调用方法
|
||
* @param callable $callable
|
||
* @param RequestParameter $request
|
||
* @param ReturnParameter $return
|
||
* @return bool
|
||
*/
|
||
private function CallMethodCallable($callable, $request, &$return)
|
||
{
|
||
if (!(is_callable($callable) && 'RequestParameter' == get_class($request) && 'ReturnParameter' == get_class($return)))
|
||
return false;
|
||
|
||
try
|
||
{
|
||
/// 2018-03-20:复制传上来的user_id到$return对象中,用于后面生成新的token
|
||
if (!empty($request->user_id))
|
||
$return->user_id = $request->user_id;
|
||
|
||
if (!($result = call_user_func_array($callable, array($request, &$return)))) /// 接口调用返回错误
|
||
throw new Exception($return->retinfo, $return->retcode);
|
||
|
||
$return->SetErrors(ERRORCODE_SUCCESS, ERRORINFO_SUCCESS);
|
||
$return->biz_content = $this->TranslateParameter($return->biz_content);
|
||
|
||
/// 2017-08-10:当接口调用成功,则返回新的用户授权信息。
|
||
$return->user_auth_token = $this->CreateUserAuthToken($return->user_id, $request->method);
|
||
/// 当忽略token时,则不返回生成的token
|
||
if ($this->MethodIsIgnoreUserAuthToken)
|
||
$return->user_auth_token = '';
|
||
|
||
if (ERRORCODE_SUCCESS != $return->retcode)
|
||
$this->SetErrors($return->retcode, $return->retinfo);
|
||
|
||
$return->app_auth_token = '';
|
||
|
||
return $result;
|
||
}
|
||
catch(Exception $Exception)
|
||
{
|
||
$return->retcode = ERRORCODE_SUCCESS == $Exception->GetCode() ? ERRORCODE_UNKNOWN : $Exception->GetCode();
|
||
$return->retinfo = ERRORINFO_SUCCESS == $Exception->GetMessage() ? ERRORINFO_UNKNOWN : $Exception->GetMessage();
|
||
|
||
$return->biz_content = (object)null; /// 置空返回数据域
|
||
|
||
/// 2017-11-14:增加对登录请求的处理,当本次请求为登录请求时,必须登录成功才生成新的用户授权信息。
|
||
$return->user_auth_token = $this->CreateUserAuthToken($return->user_id, $request->method);
|
||
/// 必须登录成功才生成新的用户授权信息。
|
||
if ($this->MethodIsLogin || $this->MethodIsIgnoreUserAuthToken)
|
||
$return->user_auth_token = '';
|
||
|
||
$return->app_auth_token = '';
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 分发函数.
|
||
* @param array $parameter
|
||
* @return ReturnParameter
|
||
* @throws Exception
|
||
* @auther bahamut
|
||
*/
|
||
public function DispatchProcedure($parameter)
|
||
{
|
||
$request = null;
|
||
$return = null;
|
||
try
|
||
{
|
||
/// 转换参数,参数名全部转成小写,方便访问。
|
||
$p = $this->TranslateParameter($parameter);
|
||
if (!is_array($p) || 0 == count($p))
|
||
//return new ReturnParameter(array('retcode' => ERRORCODE_NOPARAMETER, 'retinfo' => ERRORINFO_NOPARAMETER));
|
||
throw new Exception(ERRORINFO_NOPARAMETER, ERRORCODE_NOPARAMETER);
|
||
|
||
/// 校验必须存在的参数
|
||
///$needkey = array('app_id', 'dev_id', 'method', 'format', 'charset', 'version', 'biz_content');
|
||
$needkey = array('method', 'version', );
|
||
|
||
// /// 如果需要校验token则必须传入token参数
|
||
// if ($this->TokenEnabled)
|
||
// array_push($needkey, 'user_auth_token');
|
||
// /// 如果需要校验token和user_id是否匹配则必须传入user_id参数
|
||
// if ($this->ValidTokenUserId)
|
||
// array_push($needkey, 'user_id');
|
||
|
||
foreach ($needkey as $key)
|
||
{
|
||
if (!isset($p[$key]))
|
||
//return new ReturnParameter(array('retcode' => ERRORCODE_FIELDNOTFOUND, 'retinfo' => sprintf(ERRORINFO_FIELDNOTFOUND, $key)));
|
||
throw new Exception(sprintf(ERRORINFO_FIELDNOTFOUND, $key), ERRORCODE_FIELDNOTFOUND);
|
||
}
|
||
|
||
$request = new RequestParameter($p);
|
||
$return = new ReturnParameter(array('random_string' => $request->random_string, 'tag' => $request->tag, ));
|
||
|
||
/// 设置一些参数的默认值
|
||
if (empty($request->format))
|
||
$request->format = 'json';
|
||
if (empty($request->charset))
|
||
$request->charset = USEDCHARSET;
|
||
|
||
/// 转换编码
|
||
if (strcasecmp($request->charset, USEDCHARSET))
|
||
$request->biz_content = mb_convert_encoding(@$request->biz_content, USEDCHARSET, $request->charset);
|
||
|
||
/// 处理biz_content参数
|
||
if (empty(@$request->biz_content))
|
||
$request->biz_content = array();
|
||
elseif (0 == strcasecmp('json', $request->format)) /// json
|
||
{
|
||
/// decode json string
|
||
if (is_string($request->biz_content))
|
||
$request->biz_content = JsonStringToJsonObject($request->biz_content);
|
||
|
||
/// verify json data format
|
||
if (!is_array($request->biz_content) && !is_object($request->biz_content))
|
||
{
|
||
//$return->SetErrors(ERRORCODE_INVALIDJSONDATA, sprintf(ERRORINFO_INVALIDJSONDATA, $parameter['biz_content']));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_INVALIDJSONDATA, $parameter['biz_content']), ERRORCODE_INVALIDJSONDATA);
|
||
}
|
||
|
||
if (is_object($request->biz_content))
|
||
$request->biz_content = (array)$request->biz_content;
|
||
}
|
||
elseif (0 == strcasecmp('xml', $request->format)) /// xml
|
||
{
|
||
/// decode xml string
|
||
if (is_string($request->biz_content))
|
||
$request->biz_content = XmlStringToXmlObject($request->biz_content);
|
||
|
||
/// verify xml data format
|
||
if (!is_array($request->biz_content) && !is_object($request->biz_content))
|
||
{
|
||
//$return->SetErrors(ERRORCODE_INVALIDJSONDATA, sprintf(ERRORINFO_INVALIDJSONDATA, $parameter['biz_content']));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_INVALIDXMLDATA, $parameter['biz_content']), ERRORCODE_INVALIDXMLDATA);
|
||
}
|
||
|
||
if (is_object($request->biz_content))
|
||
$request->biz_content = (array)$request->biz_content;
|
||
}
|
||
else
|
||
{
|
||
//$return->SetErrors(ERRORCODE_BADPARAMETER, sprintf(ERRORINFO_BADPARAMETER, 'format', $request->format));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_BADPARAMETER, 'format', $request->format), ERRORCODE_BADPARAMETER);
|
||
}
|
||
|
||
/// 获取模块名,并调用对应类方法(包名.类名.方法名)
|
||
/// 2016-08-11:转换模块名为小写
|
||
$method_arr = explode('.', mb_strtolower(trim($request->method, ' ')));
|
||
if (count($method_arr) < 3)
|
||
{
|
||
//$return->SetErrors(ERRORCODE_INVALIDPARAMETER, sprintf(ERRORINFO_INVALIDPARAMETER, 'method'));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_INVALIDPARAMETER, 'method'), ERRORCODE_INVALIDPARAMETER);
|
||
}
|
||
|
||
/// 2018-01-22:校验签名
|
||
if ($this->SignEnabled)
|
||
{
|
||
if (!$request->ValidSignature($this->SignKey))
|
||
throw new Exception(ERRORINFO_INVALIDSIGNATURE, ERRORCODE_INVALIDSIGNATURE);
|
||
}
|
||
|
||
/// 2016-08-18:做调用前的校验
|
||
if (!$this->VerifyMethod($request, $return))
|
||
{
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception($return->retinfo, $return->retcode);
|
||
}
|
||
|
||
/// 生成调用模块对象
|
||
$object = $this->CreateCallableObject($method_arr[0], $method_arr[1], $method_arr[2], $request->version);
|
||
if (null == $object)
|
||
{
|
||
//$return->SetErrors(ERRORCODE_METHODNOTFOUND, sprintf(ERRORINFO_METHODNOTFOUND, $request->method, $request->version));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_METHODNOTFOUND, $request->method, $request->version), ERRORCODE_METHODNOTFOUND);
|
||
}
|
||
|
||
/// 生成调用模块
|
||
$callable = array(&$object, $method_arr[2]);
|
||
if (!is_callable($callable))
|
||
{
|
||
//$return->SetErrors(ERRORCODE_METHODNOTFOUND, sprintf(ERRORINFO_METHODNOTFOUND, $request->method, $request->version));
|
||
//$request = null;
|
||
//return $return;
|
||
|
||
throw new Exception(sprintf(ERRORINFO_METHODNOTFOUND, $request->method, $request->version), ERRORCODE_METHODNOTFOUND);
|
||
}
|
||
|
||
/// 调用方法。
|
||
$this->CallMethodCallable($callable, $request, $return);
|
||
}
|
||
catch (Exception $Exception)
|
||
{
|
||
/// 创建返回对象
|
||
if (!$return)
|
||
$return = new ReturnParameter(array('retcode' => $Exception->GetCode(), 'retinfo' => $Exception->GetMessage(),));
|
||
else
|
||
$return->SetErrors($Exception->GetCode(), $Exception->GetMessage());
|
||
|
||
if (ERRORCODE_SUCCESS == $return->retcode)
|
||
$return->retcode = ERRORCODE_UNKNOWN;
|
||
if (ERRORINFO_SUCCESS == $return->retinfo)
|
||
$return->retinfo = ERRORINFO_UNKNOWN;
|
||
|
||
$return->biz_content = (object)null;
|
||
}
|
||
|
||
if ($return)
|
||
{
|
||
/// 去掉user_id域
|
||
//if (isset($return->user_id))
|
||
unset($return->user_id);
|
||
|
||
/// 刷新签名
|
||
$return->RefreshSignature(isset($this->SignKey) ? $this->SignKey : '');
|
||
|
||
/// 输出日志
|
||
if (ERRORCODE_SUCCESS != $return->retcode)
|
||
OutputDebugMessage('request ==> ' . ObjectToLinkString($_REQUEST) . PHP_EOL . 'return ==> ' . JsonObjectToJsonString($return));
|
||
}
|
||
|
||
$request = null;
|
||
return $return;
|
||
}
|
||
|
||
/*******************************
|
||
* @name GetErrorCode
|
||
* @note get the last method call return code.
|
||
* @param none
|
||
* @return int
|
||
*******************************/
|
||
public function GetErrorCode()
|
||
{
|
||
return $this->ErrorCode;
|
||
}
|
||
|
||
/*******************************
|
||
* @name GetErrorInfo
|
||
* @note get the last method call return info.
|
||
* @param none
|
||
* @return string|mixed
|
||
*******************************/
|
||
public function GetErrorInfo()
|
||
{
|
||
return $this->ErrorInfo;
|
||
}
|
||
|
||
/*******************************
|
||
* @name GetErrors
|
||
* @note get the last method call return code and info.
|
||
* @param none
|
||
* @return array
|
||
*******************************/
|
||
public function GetErrors()
|
||
{
|
||
return array('ErrorCode' => $this->ErrorCode, 'ErrorInfo' => $this->ErrorInfo);
|
||
}
|
||
|
||
|
||
/*******************************
|
||
* @name SetErrors .
|
||
* @note set the last method call return code and info.
|
||
* @param int $errorcode
|
||
* @param string|mixed $errorinfo
|
||
* @return bool
|
||
*******************************/
|
||
public function SetErrors($errorcode, $errorinfo)
|
||
{
|
||
switch (gettype($errorinfo))
|
||
{
|
||
case 'object':
|
||
case 'array':
|
||
$errorinfo = JsonObjectToJsonString($errorinfo);
|
||
break;
|
||
}
|
||
|
||
$this->ErrorCode = $errorcode;
|
||
$this->ErrorInfo = Characet($errorinfo);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
*lock_thisfile:获得独享锁
|
||
*@param string $tmpFileStr 用来作为共享锁文件的文件名(可以随便起一个名字)
|
||
*@param boolean $locktype 锁类型,缺省为false(非阻塞型,也就是一旦加锁失败则直接返回false),设置为true则会一直等待加锁成功才返回
|
||
*@return resource|null 如果加锁成功,则返回锁实例(当使用unlock_thisfile方法的时候需要这个参数),加锁失败则返回false.
|
||
*/
|
||
function lock_thisfile($tmpFileStr, $locktype = false){
|
||
if($locktype == false)
|
||
$locktype = LOCK_EX|LOCK_NB;
|
||
$can_write = 0;
|
||
$lockfp = @fopen($tmpFileStr.".lock","w");
|
||
if($lockfp){
|
||
$can_write = @flock($lockfp,$locktype);
|
||
}
|
||
if($can_write){
|
||
return $lockfp;
|
||
}
|
||
else{
|
||
if($lockfp){
|
||
@fclose($lockfp);
|
||
@unlink($tmpFileStr.".lock");
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
/**
|
||
*unlock_thisfile:对先前取得的锁实例进行解锁
|
||
*@param resource $fp lock_thisfile方法的返回值
|
||
*@param string $tmpFileStr 用来作为共享锁文件的文件名(可以随便起一个名字)
|
||
*/
|
||
function unlock_thisfile($fp,$tmpFileStr){
|
||
@flock($fp,LOCK_UN);
|
||
@fclose($fp);
|
||
@fclose($fp);
|
||
@unlink($tmpFileStr.".lock");
|
||
}
|
||
|