toMap(); $map = self::exceptStream($map); $newContent = $content::fromMap($map); $class = new \ReflectionClass($newContent); foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (!$property->isStatic()) { $content->{$name} = $property->getValue($newContent); } } } private static function exceptStream($map) { if ($map instanceof StreamInterface) { return null; } elseif (\is_array($map)) { $data = []; foreach ($map as $k => $v) { if (null !== $v) { $item = self::exceptStream($v); if (null !== $item) { $data[$k] = $item; } } else { $data[$k] = $v; } } return $data; } return $map; } /** * 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); } }