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"); }