.gitignore000064400000000044146412214750006540 0ustar00vendor/ composer.lock .php_cs.cache .php_cs.dist000064400000005666146412214750007006 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.md000064400000001246146412214750006332 0ustar00English | [简体中文](README-CN.md) ![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg) ## Alibaba Cloud Tea Util for PHP ## Installation ### Composer ```bash composer require alibabacloud/tea-utils ``` ## Issues [Opening an Issue](https://github.com/aliyun/tea-util/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/tea-util) ## License [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) Copyright (c) 2009-present, Alibaba Cloud All rights reserved. README.md000064400000001167146412214750006036 0ustar00[English](README.md) | 简体中文 ![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg) ## Alibaba Cloud Tea Util for PHP ## 安装 ### Composer ```bash composer require alibabacloud/tea-utils ``` ## 问题 [提交 Issue](https://github.com/aliyun/tea-util/issues/new),不符合指南的问题可能会立即关闭。 ## 发行说明 每个版本的详细更改记录在[发行说明](./ChangeLog.txt)中。 ## 相关 * [最新源码](https://github.com/aliyun/tea-util) ## 许可证 [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) Copyright (c) 2009-present, Alibaba Cloud All rights reserved. composer.json000064400000001521146412214750007273 0ustar00{ "name": "alibabacloud/tea-utils", "description": "Alibaba Cloud Tea Utils for PHP", "type": "library", "license": "Apache-2.0", "authors": [ { "name": "Alibaba Cloud SDK", "email": "sdk-team@alibabacloud.com" } ], "support": { "source": "https://github.com/aliyun/tea-util", "issues": "https://github.com/aliyun/tea-util/issues" }, "require": { "php": ">5.5", "alibabacloud/tea": "^3.1" }, "require-dev": { "phpunit/phpunit": "~5.6" }, "autoload": { "psr-4": { "AlibabaCloud\\Tea\\Utils\\": "src" } }, "autoload-dev": { "psr-4": { "AlibabaCloud\\Tea\\Utils\\Tests\\": "tests" } }, "scripts": { "fixer": "php-cs-fixer fix ./", "test": [ "@clearCache", "phpunit --colors=always" ], "clearCache": "rm -rf cache/*" } } phpunit.xml000064400000002044146412214750006763 0ustar00 tests ./tests/Unit integration ./src src/Utils.php000064400000027742146412214750007166 0ustar00isSeekable()) { $stream->rewind(); } return $stream->getContents(); } /** * Read data from a readable stream, and parse it by JSON format. * * @param StreamInterface $stream the readable stream * * @return array the parsed result */ public static function readAsJSON($stream) { return self::parseJSON(self::readAsString($stream)); } /** * Generate a nonce string. * * @return string the nonce string */ public static function getNonce() { return md5(uniqid() . uniqid(md5(microtime(true)), true)); } /** * Get an UTC format string by current date, e.g. 'Thu, 06 Feb 2020 07:32:54 GMT'. * * @return string the UTC format string */ public static function getDateUTCString() { return gmdate('D, d M Y H:i:s T'); } /** * If not set the real, use default value. * * @param string $real * @param string $default * * @return string */ public static function defaultString($real, $default = '') { return null === $real ? $default : $real; } /** * If not set the real, use default value. * * @param int $real * @param int $default * * @return int the return number */ public static function defaultNumber($real, $default = 0) { if (null === $real) { return $default; } return \intval($real); } /** * Format a map to form string, like a=a%20b%20c. * * @param array|object $query * * @return string the form string */ public static function toFormString($query) { if (null === $query) { return ''; } if (\is_object($query)) { $query = json_decode(self::toJSONString($query), true); } return str_replace('+', '%20', http_build_query($query)); } /** * If not set the real, use default value. * * @param array|Model $object * * @return string the return string */ public static function toJSONString($object) { if (null === $object) { $object = new \stdClass(); } if ($object instanceof Model) { $object = $object->toMap(); } return json_encode($object); } /** * Check the string is empty? * * @param string $val * * @return bool if string is null or zero length, return true * * @deprecated */ public static function _empty($val) { return empty($val); } /** * Check the string is empty? * * @param string $val * * @return bool if string is null or zero length, return true * * @deprecated */ public static function emptyWithSuffix($val) { return empty($val); } /** * Check the string is empty? * * @param string $val * * @return bool if string is null or zero length, return true */ public static function empty_($val) { return empty($val); } /** * Check one string equals another one? * * @param int $left * @param int $right * * @return bool if equals, return true */ public static function equalString($left, $right) { return $left === $right; } /** * Check one number equals another one? * * @param int $left * @param int $right * * @return bool if equals, return true */ public static function equalNumber($left, $right) { return $left === $right; } /** * Check one value is unset. * * @param mixed $value * * @return bool if unset, return true */ public static function isUnset(&$value = null) { return !isset($value) || null === $value; } /** * Stringify the value of map. * * @param array $map * * @return array the new stringified map */ public static function stringifyMapValue($map) { if (null === $map) { return []; } foreach ($map as &$node) { if (is_numeric($node)) { $node = (string) $node; } elseif (null === $node) { $node = ''; } elseif (\is_bool($node)) { $node = true === $node ? 'true' : 'false'; } elseif (\is_object($node)) { $node = json_decode(json_encode($node), true); } } return $map; } /** * Anyify the value of map. * * @param array $m * * @return array the new anyfied map */ public static function anyifyMapValue($m) { return $m; } /** * Assert a value, if it is a boolean, return it, otherwise throws. * * @param mixed $value * * @return bool the boolean value */ public static function assertAsBoolean($value) { return \is_bool($value); } /** * Assert a value, if it is a string, return it, otherwise throws. * * @param mixed $value * * @return bool the string value */ public static function assertAsString($value) { return \is_string($value); } /** * Assert a value, if it is a bytes, return it, otherwise throws. * * @param mixed $value * * @return bool the bytes value */ public static function assertAsBytes($value) { if (!\is_array($value)) { return false; } $i = 0; foreach ($value as $k => $ord) { if ($k !== $i) { return false; } if (!\is_int($ord)) { return false; } if ($ord < 0 || $ord > 255) { return false; } ++$i; } return true; } /** * Assert a value, if it is a number, return it, otherwise throws. * * @param mixed $value * * @return bool the number value */ public static function assertAsNumber($value) { return is_numeric($value); } /** * Assert a value, if it is a map, return it, otherwise throws. * * @param $any * * @return array the map value */ public static function assertAsMap($any) { if (\is_array($any)) { return $any; } throw new \InvalidArgumentException('It is not a map value.'); } /** * Get user agent, if it userAgent is not null, splice it with defaultUserAgent and return, otherwise return * defaultUserAgent. * * @param string $userAgent * * @return string the string value */ public static function getUserAgent($userAgent = '') { if (empty(self::$defaultUserAgent)) { self::$defaultUserAgent = sprintf('AlibabaCloud OS/%s PHP/%s TeaDSL/1', PHP_OS, PHP_VERSION); } if (!empty($userAgent)) { return self::$defaultUserAgent . ' ' . $userAgent; } return self::$defaultUserAgent; } /** * If the code between 200 and 300, return true, or return false. * * @param int $code * * @return bool */ public static function is2xx($code) { return $code >= 200 && $code < 300; } /** * If the code between 300 and 400, return true, or return false. * * @param int $code * * @return bool */ public static function is3xx($code) { return $code >= 300 && $code < 400; } /** * If the code between 400 and 500, return true, or return false. * * @param int $code * * @return bool */ public static function is4xx($code) { return $code >= 400 && $code < 500; } /** * If the code between 500 and 600, return true, or return false. * * @param int $code * * @return bool */ public static function is5xx($code) { return $code >= 500 && $code < 600; } /** * Validate model. * * @param Model $model */ public static function validateModel($model) { if (null !== $model) { $model->validate(); } } /** * Model transforms to map[string]any. * * @param Model $model * * @return array */ public static function toMap($model) { if (null === $model) { return []; } $map = $model->toMap(); $names = $model->getName(); $vars = get_object_vars($model); foreach ($vars as $k => $v) { if (false !== strpos($k, 'Shrink') && !isset($names[$k])) { // A field that has the suffix `Shrink` and is not a Model class property. $targetKey = ucfirst(substr($k, 0, \strlen($k) - 6)); if (isset($map[$targetKey])) { // $targetKey exists in $map. $map[$targetKey] = $v; } } } return $map; } /** * Suspends the current thread for the specified number of milliseconds. * * @param int $millisecond */ public static function sleep($millisecond) { usleep($millisecond * 1000); } /** * Transform input as array. * * @param mixed $input * * @return array */ public static function toArray($input) { if (\is_array($input)) { foreach ($input as $k => &$v) { $v = self::toArray($v); } } elseif ($input instanceof Model) { $input = $input->toMap(); foreach ($input as $k => &$v) { $v = self::toArray($v); } } return $input; } /** * Assert a value, if it is a readable, return it, otherwise throws. * * @param mixed $value * * @return Stream the readable value */ public static function assertAsReadable($value) { if (\is_string($value)) { return new Stream( fopen('data://text/plain;base64,' . base64_encode($value), 'r') ); } if ($value instanceof Stream) { return $value; } throw new \InvalidArgumentException('It is not a stream value.'); } } src/Utils/RuntimeOptions.php000064400000001057146412214750012154 0ustar00assertEquals([ 115, 116, 114, 105, 110, 103, ], Utils::toBytes('string')); } public function testToString() { $this->assertEquals('string', Utils::toString([ 115, 116, 114, 105, 110, 103, ])); } public function testParseJSON() { $this->assertEquals([ 'a' => 'b', ], Utils::parseJSON('{"a":"b"}')); } public function testReadAsBytes() { $bytes = Utils::readAsBytes($this->getStream()); $this->assertEquals(123, $bytes[0]); } public function testReadAsString() { $string = Utils::readAsString($this->getStream()); $this->assertEquals($string[0], '{'); } public function testReadAsJSON() { $result = Utils::readAsJSON($this->getStream()); $this->assertEquals('http://httpbin.org/get', $result['url']); } public function testGetNonce() { $nonce1 = Utils::getNonce(); $nonce2 = Utils::getNonce(); $this->assertNotEquals($nonce1, $nonce2); } public function testGetDateUTCString() { $gmdate = Utils::getDateUTCString(); $now = time(); $this->assertTrue(abs($now - strtotime($gmdate)) <= 1); } public function testDefaultString() { $this->assertEquals('', Utils::defaultString(null)); $this->assertEquals('default', Utils::defaultString(null, 'default')); $this->assertEquals('real', Utils::defaultString('real', 'default')); } public function testDefaultNumber() { $this->assertEquals(0, Utils::defaultNumber(null)); $this->assertEquals(0, Utils::defaultNumber(0, 3)); $this->assertEquals(404, Utils::defaultNumber(null, 404)); $this->assertEquals(200, Utils::defaultNumber(200, 404)); } public function testToFormString() { $query = [ 'foo' => 'bar', 'empty' => '', 'a' => null, 'withWhiteSpace' => 'a b', ]; $this->assertEquals('foo=bar&empty=&withWhiteSpace=a%20b', Utils::toFormString($query)); $object = json_decode(json_encode($query)); $this->assertEquals('foo=bar&empty=&withWhiteSpace=a%20b', Utils::toFormString($object)); } public function testToJSONString() { $object = new \stdClass(); $this->assertJson(Utils::toJSONString($object)); } public function testEmpty() { $this->assertTrue(Utils::_empty('')); $this->assertFalse(Utils::_empty('not empty')); } public function testEqualString() { $this->assertTrue(Utils::equalString('a', 'a')); $this->assertFalse(Utils::equalString('a', 'b')); } public function testEqualNumber() { $this->assertTrue(Utils::equalNumber(1, 1)); $this->assertFalse(Utils::equalNumber(1, 2)); } public function testIsUnset() { $this->assertTrue(Utils::isUnset($a)); $b = 1; $this->assertFalse(Utils::isUnset($b)); } public function testStringifyMapValue() { $this->assertEquals([], Utils::stringifyMapValue(null)); $this->assertEquals([ 'foo' => 'bar', 'null' => '', 'true' => 'true', 'false' => 'false', 'number' => '1000', ], Utils::stringifyMapValue([ 'foo' => 'bar', 'null' => null, 'true' => true, 'false' => false, 'number' => 1000, ])); } public function testAnyifyMapValue() { $this->assertEquals([ 'foo' => 'bar', 'null' => null, 'true' => true, 'false' => false, 'number' => 1000, ], Utils::anyifyMapValue([ 'foo' => 'bar', 'null' => null, 'true' => true, 'false' => false, 'number' => 1000, ])); } public function testAssertAsBoolean() { $this->assertTrue(Utils::assertAsBoolean(true)); $this->assertFalse(Utils::assertAsBoolean('true')); } public function testAssertAsString() { $this->assertTrue(Utils::assertAsString('123')); $this->assertFalse(Utils::assertAsString(123)); } public function testAssertAsBytes() { // failed because $var is not array $this->assertFalse(Utils::assertAsBytes('test')); // failed because $var is map not array $this->assertFalse(Utils::assertAsBytes(['foo' => 1])); // failed because item value is not int $this->assertFalse(Utils::assertAsBytes(['1'])); // failed because item value is out off range $this->assertFalse(Utils::assertAsBytes([256])); // success $this->assertTrue(Utils::assertAsBytes([1, 2, 3])); $this->assertTrue(Utils::assertAsBytes(Utils::toBytes('string'))); } public function testAssertAsNumber() { $this->assertTrue(Utils::assertAsNumber(123)); $this->assertFalse(Utils::assertAsNumber('string')); } public function testAssertAsMap() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('It is not a map value.'); Utils::assertAsMap('is not array'); try { $map = ['foo' => 'bar']; $this->assertEquals($map, Utils::assertAsMap($map)); } catch (\Exception $e) { // should not be here $this->assertTrue(false); } } public function testGetUserAgent() { $this->assertTrue(false !== strpos(Utils::getUserAgent('CustomUserAgent'), 'CustomUserAgent')); } public function testIs2xx() { $this->assertTrue(Utils::is2xx(200)); $this->assertFalse(Utils::is2xx(300)); } public function testIs3xx() { $this->assertTrue(Utils::is3xx(300)); $this->assertFalse(Utils::is3xx(400)); } public function testIs4xx() { $this->assertTrue(Utils::is4xx(400)); $this->assertFalse(Utils::is4xx(500)); } public function testIs5xx() { $this->assertTrue(Utils::is5xx(500)); $this->assertFalse(Utils::is5xx(600)); } public function testToMap() { $from = new RequestTest(); $from->query = new RequestTestQuery([ 'booleanParamInQuery' => true, 'mapParamInQuery' => [1, 2, 3], ]); $this->assertTrue($from->query->booleanParamInQuery); $this->assertEquals([1, 2, 3], $from->query->mapParamInQuery); $target = new RequestShrinkTest([]); $this->convert($from, $target); $this->assertEquals([ 'BooleanParamInQuery' => true, 'MapParamInQuery' => [1, 2, 3], ], $target->query->toMap()); $target->query->mapParamInQueryShrink = json_encode($from->query->mapParamInQuery); $this->assertEquals([ 'BooleanParamInQuery' => true, 'MapParamInQuery' => '[1,2,3]', ], Utils::toMap($target->query)); } public function testSleep() { $before = microtime(true) * 1000; Utils::sleep(1000); $after = microtime(true) * 1000; $sub = $after - $before; $this->assertTrue(990 <= $sub && $sub <= 1100); } public function testToArray() { $model = new RequestTest(); $model->query = 'foo'; $this->assertEquals([ ['query' => 'foo'], ], Utils::toArray([$model])); $subModel = new RequestTest(); $subModel->query = 'bar'; $model->query = $subModel; $this->assertEquals([ ['query' => ['query' => 'bar']], ], Utils::toArray([$model])); } public function testAssertAsReadable() { $s0 = Utils::assertAsReadable('string content'); $this->assertTrue($s0 instanceof Stream); $s1 = Utils::assertAsReadable($s0); $this->assertEquals($s1, $s0); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('It is not a stream value.'); Utils::assertAsReadable(0); } private function convert($body, &$content) { $class = new \ReflectionClass($body); foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (!$property->isStatic()) { $value = $property->getValue($body); if ($value instanceof StreamInterface) { continue; } $content->{$name} = $value; } } } } /** * @internal * @coversNothing */ class RequestTest extends Model { /** * @var RequestTestQuery */ public $query; } /** * @internal * @coversNothing */ class RequestShrinkTest extends Model { /** * @var RequestTestShrinkQuery */ public $query; } class RequestTestQuery extends Model { /** * @description test * * @var bool */ public $booleanParamInQuery; /** * @description test * * @var array */ public $mapParamInQuery; protected $_name = [ 'booleanParamInQuery' => 'BooleanParamInQuery', 'mapParamInQuery' => 'MapParamInQuery', ]; public function toMap() { $res = []; if (null !== $this->booleanParamInQuery) { $res['BooleanParamInQuery'] = $this->booleanParamInQuery; } if (null !== $this->mapParamInQuery) { $res['MapParamInQuery'] = $this->mapParamInQuery; } return $res; } } class RequestTestShrinkQuery extends Model { /** * @description test * * @var float */ public $booleanParamInQuery; /** * @description test * * @var string */ public $mapParamInQueryShrink; protected $_name = [ 'booleanParamInQuery' => 'BooleanParamInQuery', 'mapParamInQueryShrink' => 'MapParamInQuery', ]; public function toMap() { $res = []; if (null !== $this->booleanParamInQuery) { $res['BooleanParamInQuery'] = $this->booleanParamInQuery; } if (null !== $this->mapParamInQueryShrink) { $res['MapParamInQuery'] = $this->mapParamInQueryShrink; } return $res; } } tests/bootstrap.php000064400000000151146412214750010437 0ustar00