.gitignore000064400000000521146412131570006536 0ustar00composer.phar /vendor/ # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file composer.lock .vscode/ .idea .DS_Store cache/ *.cache runtime/ .php_cs.cache .php_cs.dist000064400000005666146412131570007004 0ustar00setRiskyAllowed(true) ->setIndent(' ') ->setRules([ '@PSR2' => true, '@PhpCsFixer' => true, '@Symfony:risky' => true, 'concat_space' => ['spacing' => 'one'], 'array_syntax' => ['syntax' => 'short'], 'array_indentation' => true, 'combine_consecutive_unsets' => true, 'method_separation' => true, 'single_quote' => true, 'declare_equal_normalize' => true, 'function_typehint_space' => true, 'hash_to_slash_comment' => true, 'include' => true, 'lowercase_cast' => true, 'no_multiline_whitespace_before_semicolons' => true, 'no_leading_import_slash' => true, 'no_multiline_whitespace_around_double_arrow' => true, 'no_spaces_around_offset' => true, 'no_unneeded_control_parentheses' => true, 'no_unused_imports' => true, 'no_whitespace_before_comma_in_array' => true, 'no_whitespace_in_blank_line' => true, 'object_operator_without_whitespace' => true, 'single_blank_line_before_namespace' => true, 'single_class_element_per_statement' => true, 'space_after_semicolon' => true, 'standardize_not_equals' => true, 'ternary_operator_spaces' => true, 'trailing_comma_in_multiline_array' => true, 'trim_array_spaces' => true, 'unary_operator_spaces' => true, 'whitespace_after_comma_in_array' => true, 'no_extra_consecutive_blank_lines' => [ 'curly_brace_block', 'extra', 'parenthesis_brace_block', 'square_brace_block', 'throw', 'use', ], 'binary_operator_spaces' => [ 'align_double_arrow' => true, 'align_equals' => true, ], 'braces' => [ 'allow_single_line_closure' => true, ], ]) ->setFinder( PhpCsFixer\Finder::create() ->exclude('vendor') ->exclude('tests') ->in(__DIR__) ); README-CN.md000064400000001174146412131570006330 0ustar00[English](README.md) | 简体中文 ![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg) ## Alibaba Cloud OpenApi Util ## 安装 ### Composer ```bash composer require alibabacloud/openapi-util ``` ## 问题 [提交 Issue](https://github.com/aliyun/openapiutil/issues/new),不符合指南的问题可能会立即关闭。 ## 发行说明 每个版本的详细更改记录在[发行说明](./ChangeLog.txt)中。 ## 相关 * [最新源码](https://github.com/aliyun/openapiutil) ## 许可证 [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) Copyright (c) 2009-present, Alibaba Cloud All rights reserved. README.md000064400000001253146412131570006030 0ustar00English | [简体中文](README-CN.md) ![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg) ## Alibaba Cloud OpenApi Util ## Installation ### Composer ```bash composer require alibabacloud/openapi-util ``` ## Issues [Opening an Issue](https://github.com/aliyun/openapiutil/issues/new), Issues not conforming to the guidelines may be closed immediately. ## Changelog Detailed changes for each release are documented in the [release notes](./ChangeLog.txt). ## References * [Latest Release](https://github.com/aliyun/openapiutil) ## License [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) Copyright (c) 2009-present, Alibaba Cloud All rights reserved. autoload.php000064400000000721146412131570007071 0ustar005.5", "alibabacloud/tea": "^3.1", "alibabacloud/tea-utils": "^0.2", "lizhichao/one-sm": "^1.5" }, "require-dev": { "phpunit/phpunit": "*" }, "autoload": { "psr-4": { "AlibabaCloud\\OpenApiUtil\\": "src" } }, "autoload-dev": { "psr-4": { "AlibabaCloud\\OpenApiUtil\\Tests\\": "tests" } }, "scripts": { "fixer": "php-cs-fixer fix ./", "test": [ "@clearCache", "./vendor/bin/phpunit --colors=always" ], "clearCache": "rm -rf cache/*" }, "config": { "sort-packages": true, "preferred-install": "dist", "optimize-autoloader": true }, "prefer-stable": true } phpunit.xml000064400000002037146412131570006763 0ustar00 tests ./tests integration ./src src/OpenApiUtilClient.php000064400000036643146412131570011414 0ustar00getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (!$property->isStatic()) { $value = $property->getValue($body); if ($value instanceof StreamInterface) { continue; } $content->{$name} = $value; } } } /** * Get the string to be signed according to request. * * @param Request $request which contains signed messages * * @return string the signed string */ public static function getStringToSign($request) { $pathname = $request->pathname ?: ''; $query = $request->query ?: []; $accept = isset($request->headers['accept']) ? $request->headers['accept'] : ''; $contentMD5 = isset($request->headers['content-md5']) ? $request->headers['content-md5'] : ''; $contentType = isset($request->headers['content-type']) ? $request->headers['content-type'] : ''; $date = isset($request->headers['date']) ? $request->headers['date'] : ''; $result = $request->method . "\n" . $accept . "\n" . $contentMD5 . "\n" . $contentType . "\n" . $date . "\n"; $canonicalizedHeaders = self::getCanonicalizedHeaders($request->headers); $canonicalizedResource = self::getCanonicalizedResource($pathname, $query); return $result . $canonicalizedHeaders . $canonicalizedResource; } /** * Get signature according to stringToSign, secret. * * @param string $stringToSign the signed string * @param string $secret accesskey secret * * @return string the signature */ public static function getROASignature($stringToSign, $secret) { return base64_encode(hash_hmac('sha1', $stringToSign, $secret, true)); } /** * Parse filter into a form string. * * @param array $filter object * * @return string the string */ public static function toForm($filter) { $query = $filter; if (null === $query) { return ''; } if ($query instanceof Model) { $query = $query->toMap(); } $tmp = []; foreach ($query as $k => $v) { if (0 !== strpos($k, '_')) { $tmp[$k] = $v; } } $res = self::flatten($tmp); ksort($res); return http_build_query($res); } /** * Get timestamp. * * @return string the timestamp string */ public static function getTimestamp() { return gmdate('Y-m-d\\TH:i:s\\Z'); } /** * Parse filter into a object which's type is map[string]string. * * @param array $filter query param * * @return array the object */ public static function query($filter) { if (null === $filter) { return []; } $dict = $filter; if ($dict instanceof Model) { $dict = $dict->toMap(); } $tmp = []; foreach ($dict as $k => $v) { if (0 !== strpos($k, '_')) { $tmp[$k] = $v; } } return self::flatten($tmp); } /** * Get signature according to signedParams, method and secret. * * @param array $signedParams params which need to be signed * @param string $method http method e.g. GET * @param string $secret AccessKeySecret * * @return string the signature */ public static function getRPCSignature($signedParams, $method, $secret) { $secret .= '&'; $strToSign = self::getRpcStrToSign($method, $signedParams); $signMethod = 'HMAC-SHA1'; return self::encode($signMethod, $strToSign, $secret); } /** * Parse object into a string with specified style. * * @style specified style e.g. repeatList * * @param mixed $object the object * @param string $prefix the prefix string * @param string $style * * @return string the string */ public static function arrayToStringWithSpecifiedStyle($object, $prefix, $style) { if (null === $object) { return ''; } if ('repeatList' === $style) { return self::toForm([$prefix => $object]); } if ('simple' == $style || 'spaceDelimited' == $style || 'pipeDelimited' == $style) { $strs = self::flatten($object); switch ($style) { case 'spaceDelimited': return implode(' ', $strs); case 'pipeDelimited': return implode('|', $strs); default: return implode(',', $strs); } } elseif ('json' === $style) { self::parse($object, $parsed); return json_encode($parsed); } return ''; } /** * Transform input as array. * * @param mixed $input * * @return array */ public static function parseToArray($input) { self::parse($input, $result); return $result; } /** * Transform input as map. * * @param mixed $input * * @return array */ public static function parseToMap($input) { self::parse($input, $result); return $result; } public static function getEndpoint($endpoint, $useAccelerate, $endpointType = 'public') { if ('internal' == $endpointType) { $tmp = explode('.', $endpoint); $tmp[0] .= '-internal'; $endpoint = implode('.', $tmp); } if ($useAccelerate && 'accelerate' == $endpointType) { return 'oss-accelerate.aliyuncs.com'; } return $endpoint; } /** * Encode raw with base16. * * @param int[] $raw encoding data * * @return string encoded string */ public static function hexEncode($raw) { if (is_array($raw)) { $raw = Utils::toString($raw); } return bin2hex($raw); } /** * Hash the raw data with signatureAlgorithm. * * @param int[] $raw hashing data * @param string $signatureAlgorithm the autograph method * * @return array hashed bytes */ public static function hash($raw, $signatureAlgorithm) { $str = Utils::toString($raw); switch ($signatureAlgorithm) { case 'ACS3-HMAC-SHA256': case 'ACS3-RSA-SHA256': $res = hash('sha256', $str, true); return Utils::toBytes($res); case 'ACS3-HMAC-SM3': $res = self::sm3($str); return Utils::toBytes(hex2bin($res)); } return []; } /** * Get the authorization. * * @param Request $request request params * @param string $signatureAlgorithm the autograph method * @param string $payload the hashed request * @param string $accesskey the accessKey string * @param string $accessKeySecret the accessKeySecret string * * @return string authorization string * @throws \ErrorException * */ public static function getAuthorization($request, $signatureAlgorithm, $payload, $accesskey, $accessKeySecret) { $canonicalURI = $request->pathname ? $request->pathname : '/'; $query = $request->query ?: []; $method = strtoupper($request->method); $canonicalQueryString = self::getCanonicalQueryString($query); $signHeaders = []; foreach ($request->headers as $k => $v) { $k = strtolower($k); if (0 === strpos($k, 'x-acs-') || 'host' === $k || 'content-type' === $k) { $signHeaders[$k] = $v; } } ksort($signHeaders); $headers = []; foreach ($request->headers as $k => $v) { $k = strtolower($k); if (0 === strpos($k, 'x-acs-') || 'host' === $k || 'content-type' === $k) { $headers[$k] = trim($v); } } $canonicalHeaderString = ''; ksort($headers); foreach ($headers as $k => $v) { $canonicalHeaderString .= $k . ':' . trim(self::filter($v)) . "\n"; } if (empty($canonicalHeaderString)) { $canonicalHeaderString = "\n"; } $canonicalRequest = $method . "\n" . $canonicalURI . "\n" . $canonicalQueryString . "\n" . $canonicalHeaderString . "\n" . implode(';', array_keys($signHeaders)) . "\n" . $payload; $strtosign = $signatureAlgorithm . "\n" . self::hexEncode(self::hash(Utils::toBytes($canonicalRequest), $signatureAlgorithm)); $signature = self::sign($accessKeySecret, $strtosign, $signatureAlgorithm); $signature = self::hexEncode($signature); return $signatureAlgorithm . ' Credential=' . $accesskey . ',SignedHeaders=' . implode(';', array_keys($signHeaders)) . ',Signature=' . $signature; } public static function sign($secret, $str, $algorithm) { $result = ''; switch ($algorithm) { case 'ACS3-HMAC-SHA256': $result = hash_hmac('sha256', $str, $secret, true); break; case 'ACS3-HMAC-SM3': $result = self::hmac_sm3($str, $secret, true); break; case 'ACS3-RSA-SHA256': $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . $secret . "\n-----END RSA PRIVATE KEY-----"; @openssl_sign($str, $result, $privateKey, OPENSSL_ALGO_SHA256); } return Utils::toBytes($result); } /** * Get encoded path. * * @param string $path the raw path * * @return string encoded path */ public static function getEncodePath($path) { $tmp = explode('/', $path); foreach ($tmp as &$t) { $t = rawurlencode($t); } return implode('/', $tmp); } /** * Get encoded param. * * @param string $param the raw param * * @return string encoded param */ public static function getEncodeParam($param) { return rawurlencode($param); } private static function getRpcStrToSign($method, $query) { ksort($query); $params = []; foreach ($query as $k => $v) { if (null !== $v) { $k = rawurlencode($k); $v = rawurlencode($v); $params[] = $k . '=' . (string)$v; } } $str = implode('&', $params); return $method . '&' . rawurlencode('/') . '&' . rawurlencode($str); } private static function encode($signMethod, $strToSign, $secret) { switch ($signMethod) { case 'HMAC-SHA256': return base64_encode(hash_hmac('sha256', $strToSign, $secret, true)); default: return base64_encode(hash_hmac('sha1', $strToSign, $secret, true)); } } /** * @param array $items * @param string $delimiter * @param string $prepend * * @return array */ private static function flatten($items = [], $delimiter = '.', $prepend = '') { $flatten = []; foreach ($items as $key => $value) { $pos = \is_int($key) ? $key + 1 : $key; if ($value instanceof Model) { $value = $value->toMap(); } elseif (\is_object($value)) { $value = get_object_vars($value); } if (\is_array($value) && !empty($value)) { $flatten = array_merge( $flatten, self::flatten($value, $delimiter, $prepend . $pos . $delimiter) ); } else { if (\is_bool($value)) { $value = true === $value ? 'true' : 'false'; } $flatten[$prepend . $pos] = $value; } } return $flatten; } private static function getCanonicalizedHeaders($headers, $prefix = 'x-acs-') { ksort($headers); $str = ''; foreach ($headers as $k => $v) { if (0 === strpos(strtolower($k), $prefix)) { $str .= $k . ':' . trim(self::filter($v)) . "\n"; } } return $str; } private static function getCanonicalizedResource($pathname, $query) { if (0 === \count($query)) { return $pathname; } ksort($query); $tmp = []; foreach ($query as $k => $v) { if (!empty($v)) { $tmp[] = $k . '=' . $v; } else { $tmp[] = $k; } } return $pathname . '?' . implode('&', $tmp); } private static function parse($input, &$output) { if (null === $input || '' === $input) { $output = []; } $recursive = function ($input) use (&$recursive) { if ($input instanceof Model) { $input = $input->toMap(); } elseif (\is_object($input)) { $input = get_object_vars($input); } if (!\is_array($input)) { return $input; } $data = []; foreach ($input as $k => $v) { $data[$k] = $recursive($v); } return $data; }; $output = $recursive($input); if (!\is_array($output)) { $output = [$output]; } } private static function filter($str) { return str_replace(["\t", "\n", "\r", "\f"], '', $str); } private static function hmac_sm3($data, $key, $raw_output = false) { $pack = 'H' . \strlen(self::sm3('test')); $blocksize = 64; if (\strlen($key) > $blocksize) { $key = pack($pack, self::sm3($key)); } $key = str_pad($key, $blocksize, \chr(0x00)); $ipad = $key ^ str_repeat(\chr(0x36), $blocksize); $opad = $key ^ str_repeat(\chr(0x5C), $blocksize); $hmac = self::sm3($opad . pack($pack, self::sm3($ipad . $data))); return $raw_output ? pack($pack, $hmac) : $hmac; } private static function sm3($message) { return (new Sm3())->sign($message); } private static function getCanonicalQueryString($query) { ksort($query); $params = []; foreach ($query as $k => $v) { if (null === $v) { continue; } $str = rawurlencode($k); if ('' !== $v && null !== $v) { $str .= '=' . rawurlencode($v); } else { $str .= '='; } $params[] = $str; } return implode('&', $params); } } tests/OpenApiUtilClientTest.php000064400000031621146412131570012616 0ustar00a = 'foo'; $output = new MockModel(); OpenApiUtilClient::convert($model, $output); $this->assertEquals($model->a, $output->a); } public function testGetStringToSign() { $request = new Request(); $request->method = 'GET'; $request->pathname = '/'; $request->headers['accept'] = 'application/json'; $this->assertEquals("GET\napplication/json\n\n\n\n/", OpenApiUtilClient::getStringToSign($request)); $request->headers = [ 'accept' => 'application/json', 'content-md5' => 'md5', 'content-type' => 'application/json', 'date' => 'date', ]; $this->assertEquals("GET\napplication/json\nmd5\napplication/json\ndate\n/", OpenApiUtilClient::getStringToSign($request)); $request->headers = [ 'accept' => 'application/json', 'content-md5' => 'md5', 'content-type' => 'application/json', 'date' => 'date', 'x-acs-custom-key' => 'any value', ]; $this->assertEquals("GET\napplication/json\nmd5\napplication/json\ndate\nx-acs-custom-key:any value\n/", OpenApiUtilClient::getStringToSign($request)); $request->query = [ 'key' => 'val ue with space', ]; $this->assertEquals("GET\napplication/json\nmd5\napplication/json\ndate\nx-acs-custom-key:any value\n/?key=val ue with space", OpenApiUtilClient::getStringToSign($request)); } public function testGetROASignature() { $this->assertEquals('OmuTAr79tpI6CRoAdmzKRq5lHs0=', OpenApiUtilClient::getROASignature('stringtosign', 'secret')); } public function testToForm() { $this->assertEquals('bool=true&client=test&strs.1=str1&strs.2=str2&strs.3=false&tag.key=value', OpenApiUtilClient::toForm([ 'client' => 'test', 'tag' => [ 'key' => 'value', ], 'strs' => ['str1', 'str2', false], 'bool' => true, 'null' => null, ])); } public function testGetTimestamp() { $date = OpenApiUtilClient::getTimestamp(); $this->assertEquals(20, \strlen($date)); } public function testQuery() { $model = new MockModel(); $model->a = 'foo'; $model->c = 'boo'; $model->r = true; $array = [ 'a' => 'a', 'b1' => [ 'a' => 'a', ], 'b2' => [ 'a' => 'a', ], 'c' => ['x', 'y', 'z'], 'd' => [ $model ], 'e' => true, 'f' => null, ]; $this->assertEquals([ 'a' => 'a', 'b1.a' => 'a', 'b2.a' => 'a', 'c.1' => 'x', 'c.2' => 'y', 'c.3' => 'z', 'd.1.A' => 'foo', 'd.1.b' => '', 'd.1.c' => 'boo', 'd.1.c' => 'boo', 'd.1.r' => 'true', 'e' => 'true', 'f' => null ], OpenApiUtilClient::query($array)); } public function testGetRPCSignature() { $request = new Request(); $request->pathname = ''; $request->query = [ 'query' => 'test', 'body' => 'test', ]; $this->assertEquals('XlUyV4sXjOuX5FnjUz9IF9tm5rU=', OpenApiUtilClient::getRPCSignature($request->query, $request->method, 'secret')); } public function testArrayToStringWithSpecifiedStyle() { $data = ['ok', 'test', 2, 3]; $this->assertEquals( 'instance.1=ok&instance.2=test&instance.3=2&instance.4=3', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'repeatList' ) ); $this->assertEquals( '["ok","test",2,3]', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'json' ) ); $test = new ParseModel([ 'str' => 'A', 'model' => new ParseModel(['str' => 'sub model']), 'array' => [1, 2, 3], ]); $this->assertEquals( '{"str":"A","model":{"str":"sub model","model":null,"array":null},"array":[1,2,3]}', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $test, 'instance', 'json' ) ); // model item in array $test = [ new ParseModel([ 'str' => 'A', ]), ]; $this->assertEquals( '[{"str":"A","model":null,"array":null}]', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $test, 'instance', 'json' ) ); // model item in map $test = [ 'model' => new ParseModel([ 'str' => 'A', ]), ]; $this->assertEquals( '{"model":{"str":"A","model":null,"array":null}}', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $test, 'instance', 'json' ) ); $this->assertEquals( 'ok,test,2,3', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'simple' ) ); $this->assertEquals( 'ok test 2 3', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'spaceDelimited' ) ); $this->assertEquals( 'ok|test|2|3', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'pipeDelimited' ) ); $this->assertEquals( '', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( $data, 'instance', 'piDelimited' ) ); $this->assertEquals( '', OpenApiUtilClient::arrayToStringWithSpecifiedStyle( null, 'instance', 'pipeDelimited' ) ); } public function testParseToArray() { $test = $this->parseData(); $data = $test['data']; $expected = $test['expected']; foreach ($data as $index => $item) { $this->assertEquals($expected[$index], OpenApiUtilClient::parseToArray($item)); } } public function testParseToMap() { $test = $this->parseData(); $data = $test['data']; $expected = $test['expected']; foreach ($data as $index => $item) { $this->assertEquals($expected[$index], OpenApiUtilClient::parseToMap($item)); } } public function testGetEndpoint() { $endpoint = 'ecs.cn-hangzhou.aliyun.cs.com'; $useAccelerate = false; $endpointType = 'public'; $this->assertEquals('ecs.cn-hangzhou.aliyun.cs.com', OpenApiUtilClient::getEndpoint($endpoint, $useAccelerate, $endpointType)); $endpointType = 'internal'; $this->assertEquals('ecs-internal.cn-hangzhou.aliyun.cs.com', OpenApiUtilClient::getEndpoint($endpoint, $useAccelerate, $endpointType)); $useAccelerate = true; $endpointType = 'accelerate'; $this->assertEquals('oss-accelerate.aliyuncs.com', OpenApiUtilClient::getEndpoint($endpoint, $useAccelerate, $endpointType)); } public function testHexEncode() { $data = OpenApiUtilClient::hash(Utils::toBytes('test'), 'ACS3-HMAC-SHA256'); $this->assertEquals( Utils::toBytes(hex2bin('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08')), $data ); $data = OpenApiUtilClient::hash(Utils::toBytes('test'), 'ACS3-RSA-SHA256'); $this->assertEquals( Utils::toBytes(hex2bin('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08')), $data ); $data = OpenApiUtilClient::hash(Utils::toBytes('test'), 'ACS3-HMAC-SM3'); $this->assertEquals( Utils::toBytes(hex2bin('55e12e91650d2fec56ec74e1d3e4ddbfce2ef3a65890c2a19ecf88a307e76a23')), $data ); $data = OpenApiUtilClient::hash(Utils::toBytes('test'), 'ACS3-HM-SHA256'); $this->assertEquals('', Utils::toString($data)); } public function testGetEncodePath() { $this->assertEquals( '/path/%20test', OpenApiUtilClient::getEncodePath('/path/ test') ); } public function testGetEncodeParam() { $this->assertEquals( 'a%2Fb%2Fc%2F%20test', OpenApiUtilClient::getEncodeParam('a/b/c/ test') ); } public function testGetAuthorization() { $request = new Request(); $request->method = ''; $request->pathname = ''; $request->query = [ 'test' => 'ok', 'empty' => '', ]; $request->headers = [ 'x-acs-test' => 'http', 'x-acs-TEST' => 'https', ]; $res = OpenApiUtilClient::getAuthorization($request, 'ACS3-HMAC-SHA256', '55e12e91650d2fec56ec74e1d3e4ddbfce2ef3a65890c2a19ecf88a307e76a23', 'acesskey', 'secret'); $this->assertEquals('ACS3-HMAC-SHA256 Credential=acesskey,SignedHeaders=x-acs-test,Signature=0a0f89a45f1ec3537a2d1a1046c71b95513a8f1f02526056968da19b99a5b914', $res); $request->query = null; $res = OpenApiUtilClient::getAuthorization($request, 'ACS3-HMAC-SHA256', '55e12e91650d2fec56ec74e1d3e4ddbfce2ef3a65890c2a19ecf88a307e76a23', 'acesskey', 'secret'); $this->assertEquals('ACS3-HMAC-SHA256 Credential=acesskey,SignedHeaders=x-acs-test,Signature=af6d32deb090ae85a21d7183055cf18dca17751da96979cdf964f8f0853e9dd2', $res); } public function testSign() { $this->assertEquals( 'b9ff646822f41ef647c1416fa2b8408923828abc0464af6706e18db3e8553da8', OpenApiUtilClient::hexEncode(OpenApiUtilClient::sign('secret', 'source', 'ACS3-HMAC-SM3')) ); $this->assertEquals( '1d93c62698a1c26427265668e79fac099aa26c1df873669599a2fb2f272e64c9', OpenApiUtilClient::hexEncode(OpenApiUtilClient::sign('secret', 'source', 'ACS3-HMAC-SHA256')) ); } private function parseData() { return [ 'data' => [ 'NotArray', new ParseModel([ 'str' => 'A', 'model' => new ParseModel(['str' => 'sub model']), 'array' => [1, 2, 3], ]), [ // model item in array new ParseModel([ 'str' => 'A', ]), ], [ // model item in map 'model' => new ParseModel([ 'str' => 'A', ]), ], ], 'expected' => [ ['NotArray'], [ 'str' => 'A', 'model' => [ 'str' => 'sub model', 'model' => null, 'array' => null, ], 'array' => [1, 2, 3], ], [ [ 'str' => 'A', 'model' => null, 'array' => null, ], ], [ 'model' => [ 'str' => 'A', 'model' => null, 'array' => null, ], ], ], 'expectedJsonStr' => [ '["NotArray"]', 'NotArray', 'NotArray', 'NotArray', ], ]; } } class MockModel extends Model { public $a = 'A'; public $b = ''; public $c = ''; public function __construct() { $this->_name['a'] = 'A'; $this->_required['c'] = true; parent::__construct([]); } } class ParseModel extends Model { public $str; public $model; public $array; } tests/bootstrap.php000064400000000151146412131570010435 0ustar00