This commit is contained in:
2026-07-23 10:37:26 +08:00
parent 090abf48fa
commit 40add876cd
3503 changed files with 838201 additions and 5 deletions
-2
View File
@@ -1,2 +0,0 @@
*
!.gitignore
+21
View File
@@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright (c) 2016-2019 Riku Särkinen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
{
"name": "adbario/php-dot-notation",
"description": "PHP dot notation access to arrays",
"keywords": ["dotnotation", "arrayaccess"],
"homepage": "https://github.com/adbario/php-dot-notation",
"license": "MIT",
"authors": [
{
"name": "Riku Särkinen",
"email": "riku@adbar.io"
}
],
"require": {
"php": "^5.5 || ^7.0 || ^8.0",
"ext-json": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7|^6.6|^7.5|^8.5|^9.5",
"squizlabs/php_codesniffer": "^3.6"
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Adbar\\": "src"
}
}
}
+623
View File
@@ -0,0 +1,623 @@
<?php
/**
* Dot - PHP dot notation access to arrays
*
* @author Riku Särkinen <riku@adbar.io>
* @link https://github.com/adbario/php-dot-notation
* @license https://github.com/adbario/php-dot-notation/blob/2.x/LICENSE.md (MIT License)
*/
namespace Adbar;
use Countable;
use ArrayAccess;
use ArrayIterator;
use JsonSerializable;
use IteratorAggregate;
/**
* Dot
*
* This class provides a dot notation access and helper functions for
* working with arrays of data. Inspired by Laravel Collection.
*/
class Dot implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
/**
* The stored items
*
* @var array
*/
protected $items = [];
/**
* The delimiter (alternative to a '.') to be used.
*
* @var string
*/
protected $delimiter = '.';
/**
* Create a new Dot instance
*
* @param mixed $items
* @param string $delimiter
*/
public function __construct($items = [], $delimiter = '.')
{
$this->items = $this->getArrayItems($items);
$this->delimiter = strlen($delimiter) ? $delimiter : '.';
}
/**
* Set a given key / value pair or pairs
* if the key doesn't exist already
*
* @param array|int|string $keys
* @param mixed $value
*/
public function add($keys, $value = null)
{
if (is_array($keys)) {
foreach ($keys as $key => $value) {
$this->add($key, $value);
}
} elseif (is_null($this->get($keys))) {
$this->set($keys, $value);
}
}
/**
* Return all the stored items
*
* @return array
*/
public function all()
{
return $this->items;
}
/**
* Delete the contents of a given key or keys
*
* @param array|int|string|null $keys
*/
public function clear($keys = null)
{
if (is_null($keys)) {
$this->items = [];
return;
}
$keys = (array) $keys;
foreach ($keys as $key) {
$this->set($key, []);
}
}
/**
* Delete the given key or keys
*
* @param array|int|string $keys
*/
public function delete($keys)
{
$keys = (array) $keys;
foreach ($keys as $key) {
if ($this->exists($this->items, $key)) {
unset($this->items[$key]);
continue;
}
$items = &$this->items;
$segments = explode($this->delimiter, $key);
$lastSegment = array_pop($segments);
foreach ($segments as $segment) {
if (!isset($items[$segment]) || !is_array($items[$segment])) {
continue 2;
}
$items = &$items[$segment];
}
unset($items[$lastSegment]);
}
}
/**
* Checks if the given key exists in the provided array.
*
* @param array $array Array to validate
* @param int|string $key The key to look for
*
* @return bool
*/
protected function exists($array, $key)
{
return array_key_exists($key, $array);
}
/**
* Flatten an array with the given character as a key delimiter
*
* @param string $delimiter
* @param array|null $items
* @param string $prepend
* @return array
*/
public function flatten($delimiter = '.', $items = null, $prepend = '')
{
$flatten = [];
if (is_null($items)) {
$items = $this->items;
}
if (!func_num_args()) {
$delimiter = $this->delimiter;
}
foreach ($items as $key => $value) {
if (is_array($value) && !empty($value)) {
$flatten = array_merge(
$flatten,
$this->flatten($delimiter, $value, $prepend.$key.$delimiter)
);
} else {
$flatten[$prepend.$key] = $value;
}
}
return $flatten;
}
/**
* Return the value of a given key
*
* @param int|string|null $key
* @param mixed $default
* @return mixed
*/
public function get($key = null, $default = null)
{
if (is_null($key)) {
return $this->items;
}
if ($this->exists($this->items, $key)) {
return $this->items[$key];
}
if (strpos($key, $this->delimiter) === false) {
return $default;
}
$items = $this->items;
foreach (explode($this->delimiter, $key) as $segment) {
if (!is_array($items) || !$this->exists($items, $segment)) {
return $default;
}
$items = &$items[$segment];
}
return $items;
}
/**
* Return the given items as an array
*
* @param mixed $items
* @return array
*/
protected function getArrayItems($items)
{
if (is_array($items)) {
return $items;
} elseif ($items instanceof self) {
return $items->all();
}
return (array) $items;
}
/**
* Check if a given key or keys exists
*
* @param array|int|string $keys
* @return bool
*/
public function has($keys)
{
$keys = (array) $keys;
if (!$this->items || $keys === []) {
return false;
}
foreach ($keys as $key) {
$items = $this->items;
if ($this->exists($items, $key)) {
continue;
}
foreach (explode($this->delimiter, $key) as $segment) {
if (!is_array($items) || !$this->exists($items, $segment)) {
return false;
}
$items = $items[$segment];
}
}
return true;
}
/**
* Check if a given key or keys are empty
*
* @param array|int|string|null $keys
* @return bool
*/
public function isEmpty($keys = null)
{
if (is_null($keys)) {
return empty($this->items);
}
$keys = (array) $keys;
foreach ($keys as $key) {
if (!empty($this->get($key))) {
return false;
}
}
return true;
}
/**
* Merge a given array or a Dot object with the given key
* or with the whole Dot object
*
* @param array|string|self $key
* @param array|self $value
*/
public function merge($key, $value = [])
{
if (is_array($key)) {
$this->items = array_merge($this->items, $key);
} elseif (is_string($key)) {
$items = (array) $this->get($key);
$value = array_merge($items, $this->getArrayItems($value));
$this->set($key, $value);
} elseif ($key instanceof self) {
$this->items = array_merge($this->items, $key->all());
}
}
/**
* Recursively merge a given array or a Dot object with the given key
* or with the whole Dot object.
*
* Duplicate keys are converted to arrays.
*
* @param array|string|self $key
* @param array|self $value
*/
public function mergeRecursive($key, $value = [])
{
if (is_array($key)) {
$this->items = array_merge_recursive($this->items, $key);
} elseif (is_string($key)) {
$items = (array) $this->get($key);
$value = array_merge_recursive($items, $this->getArrayItems($value));
$this->set($key, $value);
} elseif ($key instanceof self) {
$this->items = array_merge_recursive($this->items, $key->all());
}
}
/**
* Recursively merge a given array or a Dot object with the given key
* or with the whole Dot object.
*
* Instead of converting duplicate keys to arrays, the value from
* given array will replace the value in Dot object.
*
* @param array|string|self $key
* @param array|self $value
*/
public function mergeRecursiveDistinct($key, $value = [])
{
if (is_array($key)) {
$this->items = $this->arrayMergeRecursiveDistinct($this->items, $key);
} elseif (is_string($key)) {
$items = (array) $this->get($key);
$value = $this->arrayMergeRecursiveDistinct($items, $this->getArrayItems($value));
$this->set($key, $value);
} elseif ($key instanceof self) {
$this->items = $this->arrayMergeRecursiveDistinct($this->items, $key->all());
}
}
/**
* Merges two arrays recursively. In contrast to array_merge_recursive,
* duplicate keys are not converted to arrays but rather overwrite the
* value in the first array with the duplicate value in the second array.
*
* @param array $array1 Initial array to merge
* @param array $array2 Array to recursively merge
* @return array
*/
protected function arrayMergeRecursiveDistinct(array $array1, array $array2)
{
$merged = &$array1;
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
/**
* Return the value of a given key and
* delete the key
*
* @param int|string|null $key
* @param mixed $default
* @return mixed
*/
public function pull($key = null, $default = null)
{
if (is_null($key)) {
$value = $this->all();
$this->clear();
return $value;
}
$value = $this->get($key, $default);
$this->delete($key);
return $value;
}
/**
* Push a given value to the end of the array
* in a given key
*
* @param mixed $key
* @param mixed $value
*/
public function push($key, $value = null)
{
if (is_null($value)) {
$this->items[] = $key;
return;
}
$items = $this->get($key);
if (is_array($items) || is_null($items)) {
$items[] = $value;
$this->set($key, $items);
}
}
/**
* Replace all values or values within the given key
* with an array or Dot object
*
* @param array|string|self $key
* @param array|self $value
*/
public function replace($key, $value = [])
{
if (is_array($key)) {
$this->items = array_replace($this->items, $key);
} elseif (is_string($key)) {
$items = (array) $this->get($key);
$value = array_replace($items, $this->getArrayItems($value));
$this->set($key, $value);
} elseif ($key instanceof self) {
$this->items = array_replace($this->items, $key->all());
}
}
/**
* Set a given key / value pair or pairs
*
* @param array|int|string $keys
* @param mixed $value
*/
public function set($keys, $value = null)
{
if (is_array($keys)) {
foreach ($keys as $key => $value) {
$this->set($key, $value);
}
return;
}
$items = &$this->items;
foreach (explode($this->delimiter, $keys) as $key) {
if (!isset($items[$key]) || !is_array($items[$key])) {
$items[$key] = [];
}
$items = &$items[$key];
}
$items = $value;
}
/**
* Replace all items with a given array
*
* @param mixed $items
*/
public function setArray($items)
{
$this->items = $this->getArrayItems($items);
}
/**
* Replace all items with a given array as a reference
*
* @param array $items
*/
public function setReference(array &$items)
{
$this->items = &$items;
}
/**
* Return the value of a given key or all the values as JSON
*
* @param mixed $key
* @param int $options
* @return string
*/
public function toJson($key = null, $options = 0)
{
if (is_string($key)) {
return json_encode($this->get($key), $options);
}
$options = $key === null ? 0 : $key;
return json_encode($this->items, $options);
}
/*
* --------------------------------------------------------------
* ArrayAccess interface
* --------------------------------------------------------------
*/
/**
* Check if a given key exists
*
* @param int|string $key
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return $this->has($key);
}
/**
* Return the value of a given key
*
* @param int|string $key
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->get($key);
}
/**
* Set a given value to the given key
*
* @param int|string|null $key
* @param mixed $value
*/
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
if (is_null($key)) {
$this->items[] = $value;
return;
}
$this->set($key, $value);
}
/**
* Delete the given key
*
* @param int|string $key
*/
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
$this->delete($key);
}
/*
* --------------------------------------------------------------
* Countable interface
* --------------------------------------------------------------
*/
/**
* Return the number of items in a given key
*
* @param int|string|null $key
* @return int
*/
#[\ReturnTypeWillChange]
public function count($key = null)
{
return count($this->get($key));
}
/*
* --------------------------------------------------------------
* IteratorAggregate interface
* --------------------------------------------------------------
*/
/**
* Get an iterator for the stored items
*
* @return \ArrayIterator
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->items);
}
/*
* --------------------------------------------------------------
* JsonSerializable interface
* --------------------------------------------------------------
*/
/**
* Return items for JSON serialization
*
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->items;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
/**
* Dot - PHP dot notation access to arrays
*
* @author Riku Särkinen <riku@adbar.io>
* @link https://github.com/adbario/php-dot-notation
* @license https://github.com/adbario/php-dot-notation/blob/2.x/LICENSE.md (MIT License)
*/
use Adbar\Dot;
if (! function_exists('dot')) {
/**
* Create a new Dot object with the given items and optional delimiter
*
* @param mixed $items
* @param string $delimiter
* @return \Adbar\Dot
*/
function dot($items, $delimiter = '.')
{
return new Dot($items, $delimiter);
}
}
+18
View File
@@ -0,0 +1,18 @@
# CHANGELOG
## 1.2.0 - 2024-10-17
- Refactor all credentials providers.
## 1.1.3 - 2020-12-24
- Require guzzle ^6.3|^7.0
## 1.0.2 - 2020-02-14
- Update Tea.
## 1.0.1 - 2019-12-30
- Supported get `Role Name` automatically.
## 1.0.0 - 2019-09-01
- Initial release of the Alibaba Cloud Credentials for PHP Version 1.0.0 on Packagist See <https://github.com/aliyun/credentials-php> for more information.
+30
View File
@@ -0,0 +1,30 @@
# CONTRIBUTING
We work hard to provide a high-quality and useful SDK for Alibaba Cloud, and
we greatly value feedback and contributions from our community. Please submit
your [issues][issues] or [pull requests][pull-requests] through GitHub.
## Tips
- The SDK is released under the [Apache license][license]. Any code you submit
will be released under that license. For substantial contributions, we may
ask you to sign a [Alibaba Documentation Corporate Contributor License
Agreement (CLA)][cla].
- We follow all of the relevant PSR recommendations from the [PHP Framework
Interop Group][php-fig]. Please submit code that follows these standards.
The [PHP CS Fixer][cs-fixer] tool can be helpful for formatting your code.
Your can use `composer fixer` to fix code.
- We maintain a high percentage of code coverage in our unit tests. If you make
changes to the code, please add, update, and/or remove tests as appropriate.
- If your code does not conform to the PSR standards, does not include adequate
tests, or does not contain a changelog document, we may ask you to update
your pull requests before we accept them. We also reserve the right to deny
any pull requests that do not align with our standards or goals.
[issues]: https://github.com/aliyun/credentials-php/issues
[pull-requests]: https://github.com/aliyun/credentials-php/pulls
[license]: http://www.apache.org/licenses/LICENSE-2.0
[cla]: https://alibaba-cla-2018.oss-cn-beijing.aliyuncs.com/Alibaba_Documentation_Open_Source_Corporate_CLA.pdf
[php-fig]: http://php-fig.org
[cs-fixer]: http://cs.sensiolabs.org/
[docs-readme]: https://github.com/aliyun/credentials-php/blob/master/README.md
+13
View File
@@ -0,0 +1,13 @@
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+88
View File
@@ -0,0 +1,88 @@
# NOTICE
<https://www.alibabacloud.com/>
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License.
A copy of the License is located at
<http://www.apache.org/licenses/LICENSE-2.0>
or in the "license" file accompanying this file. This file is distributed
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the specific language governing
permissions and limitations under the License.
# Guzzle
<https://github.com/guzzle/guzzle>
Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# jmespath.php
<https://github.com/mtdowling/jmespath.php>
Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Dot
<https://github.com/adbario/php-dot-notation>
Copyright (c) 2016-2019 Riku Särkinen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+423
View File
@@ -0,0 +1,423 @@
[English](/README.md) | 简体中文
![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg)
# Alibaba Cloud Credentials for PHP
[![PHP CI](https://github.com/aliyun/credentials-php/actions/workflows/ci.yml/badge.svg)](https://github.com/aliyun/credentials-php/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/aliyun/credentials-php/graph/badge.svg?token=YIkSjtfKbB)](https://codecov.io/gh/aliyun/credentials-php)
[![Latest Stable Version](https://poser.pugx.org/alibabacloud/credentials/v/stable)](https://packagist.org/packages/alibabacloud/credentials)
[![composer.lock](https://poser.pugx.org/alibabacloud/credentials/composerlock)](https://packagist.org/packages/alibabacloud/credentials)
[![Total Downloads](https://poser.pugx.org/alibabacloud/credentials/downloads)](https://packagist.org/packages/alibabacloud/credentials)
[![License](https://poser.pugx.org/alibabacloud/credentials/license)](https://packagist.org/packages/alibabacloud/credentials)
Alibaba Cloud Credentials for PHP 是帮助 PHP 开发者管理凭据的工具。
## 先决条件
您的系统需要满足[先决条件](/docs/zh-CN/0-Prerequisites.md),包括 PHP> = 5.6。 我们强烈建议使用cURL扩展,并使用TLS后端编译cURL 7.16.2+。
## 安装依赖
如果已在系统上[全局安装 Composer](https://getcomposer.org/doc/00-intro.md#globally),请直接在项目目录中运行以下内容来安装 Alibaba Cloud Credentials for PHP 作为依赖项:
```sh
composer require alibabacloud/credentials
```
> 一些用户可能由于网络问题无法安装,可以使用[阿里云 Composer 全量镜像](https://developer.aliyun.com/composer)。
请看[安装](/docs/zh-CN/1-Installation.md)有关通过 Composer 和其他方式安装的详细信息。
## 快速使用
在您开始之前,您需要注册阿里云帐户并获取您的[凭证](https://usercenter.console.aliyun.com/#/manage/ak)。
### 凭证类型
#### 使用默认凭据链
当您在初始化凭据客户端不传入任何参数时,Credentials工具会使用默认凭据链方式初始化客户端。默认凭据的读取逻辑请参见[默认凭据链](#默认凭证提供程序链)。
```php
<?php
use AlibabaCloud\Credentials\Credential;
// Chain Provider if no Parameter
$client = new Credential();
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### AccessKey
通过[用户信息管理][ak]设置 access_key,它们具有该账户完全的权限,请妥善保管。有时出于安全考虑,您不能把具有完全访问权限的主账户 AccessKey 交于一个项目的开发者使用,您可以[创建RAM子账户][ram]并为子账户[授权][permissions],使用RAM子用户的 AccessKey 来进行API调用。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
// Access Key
$config = new Config([
'type' => 'access_key',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
```
#### STS
通过安全令牌服务(Security Token Service,简称 STS),申请临时安全凭证(Temporary Security Credentials,简称 TSC),创建临时安全凭证。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'sts',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
'securityToken' => '<security_token>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### RamRoleArn
通过指定RAM角色的ARNAlibabacloud Resource Name),Credentials工具可以帮助开发者前往STS换取STS Token。您也可以通过为 `Policy` 赋值来限制RAM角色到一个更小的权限集合。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'ram_role_arn',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
// 要扮演的RAM角色ARN,示例值:acs:ram::123456789012****:role/adminrole,可以通过环境变量ALIBABA_CLOUD_ROLE_ARN设置role_arn
'roleArn' => '<role_arn>',
// 角色会话名称,可以通过环境变量ALIBABA_CLOUD_ROLE_SESSION_NAME设置role_session_name
'roleSessionName' => '<role_session_name>',
// 设置更小的权限策略,非必填。示例值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
'policy' => '',
// 设置session过期时间,非必填。
'roleSessionExpiration' => 3600,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### EcsRamRole
ECS和ECI实例均支持绑定实例RAM角色,当在实例中使用Credentials工具时,将自动获取实例绑定的RAM角色,并通过访问元数据服务获取RAM角色的STS Token,以完成凭据客户端的初始化。
实例元数据服务器支持加固模式和普通模式两种访问方式,Credentials工具默认使用加固模式(IMDSv2)获取访问凭据。若使用加固模式时发生异常,您可以通过设置disableIMDSv1来执行不同的异常处理逻辑:
- 当值为false(默认值)时,会使用普通模式继续获取访问凭据。
- 当值为true时,表示只能使用加固模式获取访问凭据,会抛出异常。
服务端是否支持IMDSv2,取决于您在服务器的配置。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'ecs_ram_role',
// 选填,该ECS角色的角色名称,不填会自动获取,但是建议加上以减少请求次数,可以通过环境变量ALIBABA_CLOUD_ECS_METADATA设置role_name
'roleName' => '<role_name>',
// 选填,是否强制关闭IMDSv1,即必须使用IMDSv2加固模式,可以通过环境变量ALIBABA_CLOUD_IMDSV1_DISABLED设置
'disableIMDSv1' => true,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### OIDCRoleArn
在容器服务 Kubernetes 版中设置了Worker节点RAM角色后,对应节点内的Pod中的应用也就可以像ECS上部署的应用一样,通过元数据服务(Meta Data Server)获取关联角色的STS Token。但如果容器集群上部署的是不可信的应用(比如部署您的客户提交的应用,代码也没有对您开放),您可能并不希望它们能通过元数据服务获取Worker节点关联实例RAM角色的STS Token。为了避免影响云上资源的安全,同时又能让这些不可信的应用安全地获取所需的 STS Token,实现应用级别的权限最小化,您可以使用RRSARAM Roles for Service Account)功能。阿里云容器集群会为不同的应用Pod创建和挂载相应的服务账户OIDC Token文件,并将相关配置信息注入到环境变量中,Credentials工具通过获取环境变量的配置信息,调用STS服务的AssumeRoleWithOIDC - OIDC角色SSO时获取扮演角色的临时身份凭证接口换取绑定角色的STS Token。详情请参见[通过RRSA配置ServiceAccount的RAM权限实现Pod权限隔离](https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/use-rrsa-to-authorize-pods-to-access-different-cloud-services#task-2142941)。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'oidc_role_arn',
// OIDC提供商ARN,可以通过环境变量ALIBABA_CLOUD_OIDC_PROVIDER_ARN设置oidc_provider_arn
'oidcProviderArn' => '<oidc_provider_arn>',
// OIDC Token文件路径,可以通过环境变量ALIBABA_CLOUD_OIDC_TOKEN_FILE设置oidc_token_file_path
'oidcTokenFilePath' => '<oidc_token_file_path>',
// 要扮演的RAM角色ARN,示例值:acs:ram::123456789012****:role/adminrole,可以通过环境变量ALIBABA_CLOUD_ROLE_ARN设置role_arn
'roleArn' => '<role_arn>',
// 角色会话名称,可以通过环境变量ALIBABA_CLOUD_ROLE_SESSION_NAME设置role_session_name
'roleSessionName' => '<role_session_name>',
// 设置更小的权限策略,非必填。示例值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
'policy' => '',
# 设置session过期时间
'roleSessionExpiration' => 3600,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### Credentials URI
通过指定提供凭证的自定义网络服务地址,让凭证自动申请维护 STS Token。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'credentials_uri',
// 凭证的 URI,格式为http://local_or_remote_uri/,可以通过环境变量ALIBABA_CLOUD_CREDENTIALS_URI设置credentials_uri
'credentialsURI' => '<credentials_uri>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### Bearer Token
目前只有云呼叫中心 CCC 这款产品支持 Bearer Token 的凭据初始化方式。
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'bearer',
// 填入您的Bearer Token
'bearerToken' => '<bearer_token>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getBearerToken();
```
## 默认凭证提供程序链
当您的程序开发环境和生产环境采用不同的凭据类型,常见做法是在代码中获取当前环境信息,编写获取不同凭据的分支代码。借助Credentials工具的默认凭据链,您可以用同一套代码,通过程序之外的配置来控制不同环境下的凭据获取方式。当您在不传入参数的情况下,直接使用$credential = new Credential();初始化凭据客户端时,阿里云SDK将会尝试按照如下顺序查找相关凭据信息。
### 1. 使用环境变量
Credentials工具会优先在环境变量中获取凭据信息。
- 如果系统环境变量 `ALIBABA_CLOUD_ACCESS_KEY_ID`(密钥Key`ALIBABA_CLOUD_ACCESS_KEY_SECRET`(密钥Value 不为空,Credentials工具会优先使用它们作为默认凭据。
- 如果系统环境变量 `ALIBABA_CLOUD_ACCESS_KEY_ID`(密钥Key)、`ALIBABA_CLOUD_ACCESS_KEY_SECRET`(密钥Value)、`ALIBABA_CLOUD_SECURITY_TOKEN`Token)均不为空,Credentials工具会优先使用STS Token作为默认凭据。
### 2. 使用OIDC RAM角色
若不存在优先级更高的凭据信息,Credentials工具会在环境变量中获取如下内容:
`ALIBABA_CLOUD_ROLE_ARN`RAM角色名称ARN
`ALIBABA_CLOUD_OIDC_PROVIDER_ARN`OIDC提供商ARN
`ALIBABA_CLOUD_OIDC_TOKEN_FILE`OIDC Token文件路径;
若以上三个环境变量都已设置内容,Credentials将会使用变量内容调用STS服务的[AssumeRoleWithOIDC - OIDC角色SSO时获取扮演角色的临时身份凭证](https://help.aliyun.com/zh/ram/developer-reference/api-sts-2015-04-01-assumerolewithoidc)接口换取STS Token作为默认凭据。
### 3. 使用 Aliyun CLI 工具的 config.json 配置文件
若不存在优先级更高的凭据信息,Credentials工具会优先在如下位置查找 `config.json` 文件是否存在:
Linux系统:`~/.aliyun/config.json`
Windows系统: `C:\Users\USER_NAME\.aliyun\config.json`
如果文件存在,程序将会使用配置文件中 `current` 指定的凭据信息初始化凭据客户端。当然,您也可以通过环境变量 `ALIBABA_CLOUD_PROFILE` 来指定凭据信息,例如设置 `ALIBABA_CLOUD_PROFILE` 的值为 `AK`
在config.json配置文件中每个module的值代表了不同的凭据信息获取方式:
- AK:使用用户的Access Key作为凭据信息;
- RamRoleArn:使用RAM角色的ARN来获取凭据信息;
- EcsRamRole:利用ECS绑定的RAM角色来获取凭据信息;
- OIDC:通过OIDC ARN和OIDC Token来获取凭据信息;
- ChainableRamRoleArn:采用角色链的方式,通过指定JSON文件中的其他凭据,以重新获取新的凭据信息。
配置示例信息如下:
```json
{
"current": "AK",
"profiles": [
{
"name": "AK",
"mode": "AK",
"access_key_id": "access_key_id",
"access_key_secret": "access_key_secret"
},
{
"name": "RamRoleArn",
"mode": "RamRoleArn",
"access_key_id": "access_key_id",
"access_key_secret": "access_key_secret",
"ram_role_arn": "ram_role_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
},
{
"name": "EcsRamRole",
"mode": "EcsRamRole",
"ram_role_name": "ram_role_name"
},
{
"name": "OIDC",
"mode": "OIDC",
"ram_role_arn": "ram_role_arn",
"oidc_token_file": "path/to/oidc/file",
"oidc_provider_arn": "oidc_provider_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
},
{
"name": "ChainableRamRoleArn",
"mode": "ChainableRamRoleArn",
"source_profile": "AK",
"ram_role_arn": "ram_role_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
}
]
}
```
### 4. 使用配置文件
>
> 如果用户主目录存在默认文件 `~/.alibabacloud/credentials` Windows 为 `C:\Users\USER_NAME\.alibabacloud\credentials`),程序会自动创建指定类型和名称的凭证。您也可通过环境变量 `ALIBABA_CLOUD_CREDENTIALS_FILE` 指定配置文件路径。如果文件存在,程序将会使用配置文件中 default 指定的凭据信息初始化凭据客户端。当然,您也可以通过环境变量 `ALIBABA_CLOUD_PROFILE` 来指定凭据信息,例如设置 `ALIBABA_CLOUD_PROFILE` 的值为 `client1`。
配置示例信息如下:
```ini
[default]
type = access_key # 认证方式为 access_key
access_key_id = foo # Key
access_key_secret = bar # Secret
[project1]
type = ecs_ram_role # 认证方式为 ecs_ram_role
role_name = EcsRamRoleTest # Role Name,非必填,不填则自动获取,建议设置,可以减少网络请求。
[project2]
type = ram_role_arn # 认证方式为 ram_role_arn
access_key_id = foo
access_key_secret = bar
role_arn = role_arn
role_session_name = session_name
[project3]
type=oidc_role_arn # 认证方式为 oidc_role_arn
oidc_provider_arn=oidc_provider_arn
oidc_token_file_path=oidc_token_file_path
role_arn=role_arn
role_session_name=session_name
```
### 5. 使用 ECS 实例RAM角色
若不存在优先级更高的凭据信息,Credentials工具将通过环境变量获取ALIBABA_CLOUD_ECS_METADATAECS实例RAM角色名称)的值。若该变量的值存在,程序将采用加固模式(IMDSv2)访问ECS的元数据服务(Meta Data Server),以获取ECS实例RAM角色的STS Token作为默认凭据信息。在使用加固模式时若发生异常,将使用普通模式兜底来获取访问凭据。您也可以通过设置环境变量ALIBABA_CLOUD_IMDSV1_DISABLED,执行不同的异常处理逻辑:
- 当值为false时,会使用普通模式继续获取访问凭据。
- 当值为true时,表示只能使用加固模式获取访问凭据,会抛出异常。
服务端是否支持IMDSv2,取决于您在服务器的配置。
### 6. 使用外部服务 Credentials URI
若不存在优先级更高的凭据信息,Credentials工具会在环境变量中获取ALIBABA_CLOUD_CREDENTIALS_URI,若存在,程序将请求该URI地址,获取临时安全凭证作为默认凭据信息。
外部服务响应结构应如下:
```json
{
"Code": "Success",
"AccessKeyId": "AccessKeyId",
"AccessKeySecret": "AccessKeySecret",
"SecurityToken": "SecurityToken",
"Expiration": "2024-10-26T03:46:38Z"
}
```
## 文档
* [先决条件](/docs/zh-CN/0-Prerequisites.md)
* [安装](/docs/zh-CN/1-Installation.md)
## 问题
[提交 Issue](https://github.com/aliyun/credentials-php/issues/new/choose),不符合指南的问题可能会立即关闭。
## 发行说明
每个版本的详细更改记录在[发行说明](/CHANGELOG.md)中。
## 贡献
提交 Pull Request 之前请阅读[贡献指南](/CONTRIBUTING.md)。
## 相关
* [OpenAPI 开发者门户][open-api]
* [Packagist][packagist]
* [Composer][composer]
* [Guzzle中文文档][guzzle-docs]
* [最新源码][latest-release]
## 许可证
[Apache-2.0](/LICENSE.md)
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
[open-api]: https://api.aliyun.com
[latest-release]: https://github.com/aliyun/credentials-php
[guzzle-docs]: https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html
[composer]: https://getcomposer.org
[packagist]: https://packagist.org/packages/alibabacloud/credentials
+422
View File
@@ -0,0 +1,422 @@
English | [简体中文](/README-zh-CN.md)
![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg)
# Alibaba Cloud Credentials for PHP
[![PHP CI](https://github.com/aliyun/credentials-php/actions/workflows/ci.yml/badge.svg)](https://github.com/aliyun/credentials-php/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/aliyun/credentials-php/graph/badge.svg?token=YIkSjtfKbB)](https://codecov.io/gh/aliyun/credentials-php)
[![Latest Stable Version](https://poser.pugx.org/alibabacloud/credentials/v/stable)](https://packagist.org/packages/alibabacloud/credentials)
[![composer.lock](https://poser.pugx.org/alibabacloud/credentials/composerlock)](https://packagist.org/packages/alibabacloud/credentials)
[![Total Downloads](https://poser.pugx.org/alibabacloud/credentials/downloads)](https://packagist.org/packages/alibabacloud/credentials)
[![License](https://poser.pugx.org/alibabacloud/credentials/license)](https://packagist.org/packages/alibabacloud/credentials)
Alibaba Cloud Credentials for PHP is a tool that helps PHP developers manage their credentials.
## Prerequisites
Your system needs to meet [Prerequisites](/docs/zh-CN/0-Prerequisites.md), including PHP> = 5.6. We strongly recommend using the cURL extension and compiling cURL 7.16.2+ using the TLS backend.
## Installation
If you have [Globally Install Composer](https://getcomposer.org/doc/00-intro.md#globally) on your system, install Alibaba Cloud Credentials for PHP as a dependency by running the following directly in the project directory:
```sh
composer require alibabacloud/credentials
```
> Some users may not be able to install due to network problems, you can switch to the [Alibaba Cloud Composer Mirror](https://developer.aliyun.com/composer).
See [Installation](/docs/zh-CN/1-Installation.md) for details on installing through Composer and other means.
## Quick Examples
Before you begin, you need to sign up for an Alibaba Cloud account and retrieve your [Credentials](https://usercenter.console.aliyun.com/#/manage/ak).
### Credential Type
#### Default credential provider chain
If you do not specify a method to initialize a Credentials client, the default credential provider chain is used. For more information, see the Default credential provider chain section of this topic.
```php
<?php
use AlibabaCloud\Credentials\Credential;
// Chain Provider if no Parameter
// Do not specify a method to initialize a Credentials client.
$client = new Credential();
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### AccessKey
Setup access_key credential through [User Information Management][ak], it have full authority over the account, please keep it safe. Sometimes for security reasons, you cannot hand over a primary account AccessKey with full access to the developer of a project. You may create a sub-account [RAM Sub-account][ram] , grant its [authorization][permissions]and use the AccessKey of RAM Sub-account.
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
// Access Key
$config = new Config([
'type' => 'access_key',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
```
#### STS
Create a temporary security credential by applying Temporary Security Credentials (TSC) through the Security Token Service (STS).
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'sts',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
'securityToken' => '<security_token>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### RamRoleArn
By specifying [RAM Role][RAM Role], the credential will be able to automatically request maintenance of STS Token. If you want to limit the permissions([How to make a policy][policy]) of STS Token, you can assign value for `Policy`.
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'ram_role_arn',
'accessKeyId' => '<access_key_id>',
'accessKeySecret' => '<access_key_secret>',
// Specify the ARN of the RAM role to be assumed. Example: acs:ram::123456789012****:role/adminrole.
'roleArn' => '<role_arn>',
// Specify the name of the role session.
'roleSessionName' => '<role_session_name>',
// Optional. Specify limited permissions for the RAM role. Example: {"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}.
'policy' => '',
// Optional. Specify the expiration of the session
'roleSessionExpiration' => 3600,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### EcsRamRole
Both ECS and ECI instances support binding instance RAM roles. When the Credentials tool is used in an instance, the RAM role bound to the instance will be automatically obtained, and the STS Token of the RAM role will be obtained by accessing the metadata service to complete the initialization of the credential client.
The instance metadata server supports two access modes: hardened mode and normal mode. The Credentials tool uses hardened mode (IMDSv2) by default to obtain access credentials. If an exception occurs when using hardened mode, you can set disableIMDSv1 to perform different exception handling logic:
- When the value is false (default value), the normal mode will continue to be used to obtain access credentials.
- When the value is true, it means that only hardened mode can be used to obtain access credentials, and an exception will be thrown.
Whether the server supports IMDSv2 depends on your configuration on the server.
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'ecs_ram_role',
// Optional. Specify the name of the RAM role of the ECS instance. If you do not specify this parameter, its value is automatically obtained. To reduce the number of requests, we recommend that you specify this parameter.
'roleName' => '<role_name>',
//Optional, whether to forcibly disable IMDSv1, that is, to use IMDSv2 hardening mode, which can be set by the environment variable ALIBABA_CLOUD_IMDSV1_DISABLED
'disableIMDSv1' => true,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### OIDCRoleArn
After you attach a RAM role to a worker node in an Container Service for Kubernetes, applications in the pods on the worker node can use the metadata server to obtain an STS token the same way in which applications on ECS instances do. However, if an untrusted application is deployed on the worker node, such as an application that is submitted by your customer and whose code is unavailable to you, you may not want the application to use the metadata server to obtain an STS token of the RAM role attached to the worker node. To ensure the security of cloud resources and enable untrusted applications to securely obtain required STS tokens, you can use the RAM Roles for Service Accounts (RRSA) feature to grant minimum necessary permissions to an application. In this case, the ACK cluster creates a service account OpenID Connect (OIDC) token file, associates the token file with a pod, and then injects relevant environment variables into the pod. Then, the Credentials tool uses the environment variables to call the AssumeRoleWithOIDC operation of STS and obtains an STS token of the RAM role. For more information about the RRSA feature, see [Use RRSA to authorize different pods to access different cloud services](https://www.alibabacloud.com/help/en/ack/ack-managed-and-ack-dedicated/user-guide/use-rrsa-to-authorize-pods-to-access-different-cloud-services#task-2142941).
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'oidc_role_arn',
// Specify the ARN of the OIDC IdP by specifying the ALIBABA_CLOUD_OIDC_PROVIDER_ARN environment variable.
'oidcProviderArn' => '<oidc_provider_arn>',
// Specify the path of the OIDC token file by specifying the ALIBABA_CLOUD_OIDC_TOKEN_FILE environment variable.
'oidcTokenFilePath' => '<oidc_token_file_path>',
// Specify the ARN of the RAM role by specifying the ALIBABA_CLOUD_ROLE_ARN environment variable.
'roleArn' => '<role_arn>',
// Specify the role session name by specifying the ALIBABA_CLOUD_ROLE_SESSION_NAME environment variable.
'roleSessionName' => '<role_session_name>',
// Optional. Specify limited permissions for the RAM role. Example: {"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}.
'policy' => '',
// Optional. Specify the validity period of the session.
'roleSessionExpiration' => 3600,
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### Credentials URI
By specifying the url, the credential will be able to automatically request maintenance of STS Token.
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'credentials_uri',
// Format: http url. `credentialsURI` can be replaced by setting environment variable: ALIBABA_CLOUD_CREDENTIALS_URI
'credentialsURI' => '<credentials_uri>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getAccessKeyId();
$credential->getAccessKeySecret();
$credential->getSecurityToken();
```
#### Bearer Token
If credential is required by the Cloud Call Centre (CCC), please apply for Bearer Token maintenance by yourself.
```php
<?php
use AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Credential\Config;
$config = new Config([
'type' => 'bearer',
'bearerToken' => '<bearer_token>',
]);
$client = new Credential($config);
$credential = $client->getCredential();
$credential->getBearerToken();
```
## Default credential provider chain
If you want to use different types of credentials in the development and production environments of your application, you generally need to obtain the environment information from the code and write code branches to obtain different credentials for the development and production environments. The default credential provider chain of the Credentials tool allows you to use the same code to obtain credentials for different environments based on configurations independent of the application. If you use $credential = new Credential(); to initialize a Credentials client without specifying an initialization method, the Credentials tool obtains the credential information in the following order:
### 1. Environmental certificate
Look for environment credentials in environment variable.
- If the `ALIBABA_CLOUD_ACCESS_KEY_ID` and `ALIBABA_CLOUD_ACCESS_KEY_SECRET` environment variables are defined and are not empty, the program will use them to create default credentials.
- If the `ALIBABA_CLOUD_ACCESS_KEY_ID`, `ALIBABA_CLOUD_ACCESS_KEY_SECRET` and `ALIBABA_CLOUD_SECURITY_TOKEN` environment variables are defined and are not empty, the program will use them to create temporary security credentials(STS). Note: This token has an expiration time, it is recommended to use it in a temporary environment.
### 2. The RAM role of an OIDC IdP
If no credentials are found in the previous step, the Credentials tool obtains the values of the following environment variables:
`ALIBABA_CLOUD_ROLE_ARN`: the ARN of the RAM role.
`ALIBABA_CLOUD_OIDC_PROVIDER_ARN`: the ARN of the OIDC IdP.
`ALIBABA_CLOUD_OIDC_TOKEN_FILE`: the path of the OIDC token file.
If the preceding three environment variables are specified, the Credentials tool uses the environment variables to call the [AssumeRoleWithOIDC](https://www.alibabacloud.com/help/en/ram/developer-reference/api-sts-2015-04-01-assumerolewithoidc) operation of STS to obtain an STS token as the default credential.
### 3. Using the config.json Configuration File of Aliyun CLI Tool
If there is no higher-priority credential information, the Credentials tool will first check the following locations to see if the config.json file exists:
Linux system: `~/.aliyun/config.json`
Windows system: `C:\Users\USER_NAME\.aliyun\config.json`
If the file exists, the program will use the credential information specified by `current` in the configuration file to initialize the credentials client. Of course, you can also use the environment variable `ALIBABA_CLOUD_PROFILE` to specify the credential information, for example by setting the value of `ALIBABA_CLOUD_PROFILE` to `AK`.
In the config.json configuration file, the value of each module represents different ways to obtain credential information:
- AK: Use the Access Key of the user as credential information;
- RamRoleArn: Use the ARN of the RAM role to obtain credential information;
- EcsRamRole: Use the RAM role bound to the ECS to obtain credential information;
- OIDC: Obtain credential information through OIDC ARN and OIDC Token;
- ChainableRamRoleArn: Use the role chaining method to obtain new credential information by specifying other credentials in the JSON file.
The configuration example information is as follows:
```json
{
"current": "AK",
"profiles": [
{
"name": "AK",
"mode": "AK",
"access_key_id": "access_key_id",
"access_key_secret": "access_key_secret"
},
{
"name": "RamRoleArn",
"mode": "RamRoleArn",
"access_key_id": "access_key_id",
"access_key_secret": "access_key_secret",
"ram_role_arn": "ram_role_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
},
{
"name": "EcsRamRole",
"mode": "EcsRamRole",
"ram_role_name": "ram_role_name"
},
{
"name": "OIDC",
"mode": "OIDC",
"ram_role_arn": "ram_role_arn",
"oidc_token_file": "path/to/oidc/file",
"oidc_provider_arn": "oidc_provider_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
},
{
"name": "ChainableRamRoleArn",
"mode": "ChainableRamRoleArn",
"source_profile": "AK",
"ram_role_arn": "ram_role_arn",
"ram_session_name": "ram_session_name",
"expired_seconds": 3600,
"sts_region": "cn-hangzhou"
}
]
}
```
### 4. Configuration file
>
> If the user's home directory has the default file `~/.alibabacloud/credentials` (Windows is `C:\Users\USER_NAME\.alibabacloud\credentials`), the program will automatically create credentials with the specified type and name. You can also specify the configuration file path by configuring the `ALIBABA_CLOUD_CREDENTIALS_FILE` environment variable. If the configuration file exists, the application initializes a Credentials client by using the credential information that is specified by default in the configuration file. You can also configure the `ALIBABA_CLOUD_PROFILE` environment variable to modify the default credential information that is read.
Configuration example:
```ini
[default]
type = access_key # Authentication method is access_key
access_key_id = foo # Key
access_key_secret = bar # Secret
[project1]
type = ecs_ram_role # Authentication method is ecs_ram_role
role_name = EcsRamRoleTest # Role name, optional. It will be retrieved automatically if not set. It is highly recommended to set it up to reduce requests.
[project2]
type = ram_role_arn # Authentication method is ram_role_arn
access_key_id = foo
access_key_secret = bar
role_arn = role_arn
role_session_name = session_name
[project3]
type=oidc_role_arn # Authentication method is oidc_role_arn
oidc_provider_arn=oidc_provider_arn
oidc_token_file_path=oidc_token_file_path
role_arn=role_arn
role_session_name=session_name
```
### 5. Instance RAM role
If there is no credential information with a higher priority, the Credentials tool will obtain the value of ALIBABA_CLOUD_ECS_METADATA (ECS instance RAM role name) through the environment variable. If the value of this variable exists, the program will use the hardened mode (IMDSv2) to access the metadata service (Meta Data Server) of ECS to obtain the STS Token of the ECS instance RAM role as the default credential information. If an exception occurs when using the hardened mode, the normal mode will be used as a fallback to obtain access credentials. You can also set the environment variable ALIBABA_CLOUD_IMDSV1_DISABLED to perform different exception handling logic:
- When the value is false, the normal mode will continue to obtain access credentials.
- When the value is true, it means that only the hardened mode can be used to obtain access credentials, and an exception will be thrown.
Whether the server supports IMDSv2 depends on your configuration on the server.
### 6. Using External Service Credentials URI
If there are no higher-priority credential information, the Credentials tool will obtain the `ALIBABA_CLOUD_CREDENTIALS_URI` from the environment variables. If it exists, the program will request the URI address to obtain temporary security credentials as the default credential information.
The external service response structure should be as follows:
```json
{
"Code": "Success",
"AccessKeyId": "AccessKeyId",
"AccessKeySecret": "AccessKeySecret",
"SecurityToken": "SecurityToken",
"Expiration": "2024-10-26T03:46:38Z"
}
```
## Documentation
* [Prerequisites](/docs/zh-CN/0-Prerequisites.md)
* [Installation](/docs/zh-CN/1-Installation.md)
## Issue
[Submit Issue](https://github.com/aliyun/credentials-php/issues/new/choose), Problems that do not meet the guidelines may close immediately.
## Release notes
Detailed changes for each version are recorded in the [Release Notes](/CHANGELOG.md).
## Contribution
Please read the [Contribution Guide](/CONTRIBUTING.md) before submitting a Pull Request.
## Related
* [OpenAPI Developer Portal][open-api]
* [Packagist][packagist]
* [Composer][composer]
* [Guzzle Doc][guzzle-docs]
* [Latest Release][latest-release]
## License
[Apache-2.0](/LICENSE.md)
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
[open-api]: https://api.alibabacloud.com
[latest-release]: https://github.com/aliyun/credentials-php
[guzzle-docs]: http://docs.guzzlephp.org/en/stable/request-options.html
[composer]: https://getcomposer.org
[packagist]: https://packagist.org/packages/alibabacloud/credentials
+21
View File
@@ -0,0 +1,21 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
+6
View File
@@ -0,0 +1,6 @@
Upgrading Guide
===============
1.x
-----------------------
- This is the first version. See <https://github.com/aliyun/credentials-php> for more information.
+107
View File
@@ -0,0 +1,107 @@
{
"name": "alibabacloud/credentials",
"homepage": "https://www.alibabacloud.com/",
"description": "Alibaba Cloud Credentials for PHP",
"keywords": [
"sdk",
"tool",
"cloud",
"client",
"aliyun",
"library",
"alibaba",
"Credentials",
"alibabacloud"
],
"type": "library",
"license": "Apache-2.0",
"support": {
"source": "https://github.com/aliyun/credentials-php",
"issues": "https://github.com/aliyun/credentials-php/issues"
},
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com",
"homepage": "http://www.alibabacloud.com"
}
],
"require": {
"php": ">=5.6",
"ext-curl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-openssl": "*",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"guzzlehttp/guzzle": "^6.3|^7.0",
"adbario/php-dot-notation": "^2.2",
"alibabacloud/tea": "^3.0"
},
"require-dev": {
"ext-spl": "*",
"ext-dom": "*",
"ext-pcre": "*",
"psr/cache": "^1.0",
"ext-sockets": "*",
"drupal/coder": "^8.3",
"symfony/dotenv": "^3.4",
"phpunit/phpunit": "^5.7|^6.5|^8.5|^9.3",
"monolog/monolog": "^1.24",
"composer/composer": "^1.8",
"mikey179/vfsstream": "^1.6",
"symfony/var-dumper": "^3.4"
},
"suggest": {
"ext-sockets": "To use client-side monitoring"
},
"autoload": {
"psr-4": {
"AlibabaCloud\\Credentials\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"AlibabaCloud\\Credentials\\Tests\\": "tests/"
}
},
"config": {
"preferred-install": "dist",
"optimize-autoloader": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts-descriptions": {
"cs": "Tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard.",
"cbf": "Automatically correct coding standard violations.",
"fixer": "Fixes code to follow standards.",
"test": "Run all tests.",
"unit": "Run Unit tests.",
"feature": "Run Feature tests.",
"clearCache": "Clear cache like coverage.",
"coverage": "Show Coverage html.",
"endpoints": "Update endpoints from OSS."
},
"scripts": {
"cs": "phpcs --standard=PSR2 -n ./",
"cbf": "phpcbf --standard=PSR2 -n ./",
"fixer": "php-cs-fixer fix ./",
"test": [
"phpunit --colors=always"
],
"unit": [
"@clearCache",
"phpunit --testsuite=Unit --colors=always"
],
"feature": [
"@clearCache",
"phpunit --testsuite=Feature --colors=always"
],
"coverage": "open cache/coverage/index.html",
"clearCache": "rm -rf cache/*"
}
}
@@ -0,0 +1,86 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\ShaHmac1Signature;
/**
* @deprecated
* Use the AccessKey to complete the authentication.
*/
class AccessKeyCredential implements CredentialsInterface
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* AccessKeyCredential constructor.
*
* @param string $access_key_id Access key ID
* @param string $access_key_secret Access Key Secret
*/
public function __construct($access_key_id, $access_key_secret)
{
Filter::accessKey($access_key_id, $access_key_secret);
$this->accessKeyId = $access_key_id;
$this->accessKeySecret = $access_key_secret;
}
/**
* @return string
*/
public function getAccessKeyId()
{
return $this->accessKeyId;
}
/**
* @return string
*/
public function getAccessKeySecret()
{
return $this->accessKeySecret;
}
/**
* @return string
*/
public function __toString()
{
return "$this->accessKeyId#$this->accessKeySecret";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return new ShaHmac1Signature();
}
public function getSecurityToken()
{
return '';
}
/**
* @inheritDoc
*/
public function getCredential()
{
return new CredentialModel([
'accessKeyId' => $this->accessKeyId,
'accessKeySecret' => $this->accessKeySecret,
'type' => 'access_key',
]);
}
}
@@ -0,0 +1,67 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\BearerTokenSignature;
/**
* Class BearerTokenCredential
*/
class BearerTokenCredential implements CredentialsInterface
{
/**
* @var string
*/
private $bearerToken;
/**
* BearerTokenCredential constructor.
*
* @param $bearer_token
*/
public function __construct($bearer_token)
{
Filter::bearerToken($bearer_token);
$this->bearerToken = $bearer_token;
}
/**
* @return string
*/
public function getBearerToken()
{
return $this->bearerToken;
}
/**
* @return string
*/
public function __toString()
{
return "bearerToken#$this->bearerToken";
}
/**
* @return BearerTokenSignature
*/
public function getSignature()
{
return new BearerTokenSignature();
}
/**
* @inheritDoc
*/
public function getCredential()
{
return new CredentialModel([
'bearerToken' => $this->bearerToken,
'type' => 'bearer',
]);
}
}
+268
View File
@@ -0,0 +1,268 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Credential\Config;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Providers\DefaultCredentialsProvider;
use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider;
use AlibabaCloud\Credentials\Providers\OIDCRoleArnCredentialsProvider;
use AlibabaCloud\Credentials\Providers\RamRoleArnCredentialsProvider;
use AlibabaCloud\Credentials\Providers\RsaKeyPairCredentialsProvider;
use AlibabaCloud\Credentials\Providers\StaticAKCredentialsProvider;
use AlibabaCloud\Credentials\Providers\StaticSTSCredentialsProvider;
use AlibabaCloud\Credentials\Providers\URLCredentialsProvider;
use AlibabaCloud\Credentials\Utils\Helper;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
/**
* Class Credential
*
* @package AlibabaCloud\Credentials
*
*/
class Credential
{
/**
* Version of the Client
*/
const VERSION = '1.1.5';
/**
* @var Config
*/
protected $config;
/**
* @var CredentialsInterface
*/
protected $credential;
/**
* Credential constructor.
*
* @param array|Config $config
*/
public function __construct($config = [])
{
if (\is_array($config)) {
if (empty($config)) {
$this->config = null;
} else {
$this->config = new Config($this->parseConfig($config));
}
} else {
$this->config = $config;
}
$this->credential = $this->getCredentials($this->config);
}
/**
* @param array $config
*
* @return array
*/
private function parseConfig($config)
{
$res = [];
foreach (\array_change_key_case($config) as $key => $value) {
$res[Helper::snakeToCamelCase($key)] = $value;
}
return $res;
}
/**
* Credentials getter.
*
* @param Config $config
* @return CredentialsInterface
*
*/
private function getCredentials($config)
{
if (is_null($config)) {
return new CredentialsProviderWrap('default', new DefaultCredentialsProvider());
}
switch ($config->type) {
case 'access_key':
$provider = new StaticAKCredentialsProvider([
'accessKeyId' => $config->accessKeyId,
'accessKeySecret' => $config->accessKeySecret,
]);
return new CredentialsProviderWrap('access_key', $provider);
case 'sts':
$provider = new StaticSTSCredentialsProvider([
'accessKeyId' => $config->accessKeyId,
'accessKeySecret' => $config->accessKeySecret,
'securityToken' => $config->securityToken,
]);
return new CredentialsProviderWrap('sts', $provider);
case 'bearer':
return new BearerTokenCredential($config->bearerToken);
case 'ram_role_arn':
if (!is_null($config->securityToken) && $config->securityToken !== '') {
$innerProvider = new StaticSTSCredentialsProvider([
'accessKeyId' => $config->accessKeyId,
'accessKeySecret' => $config->accessKeySecret,
'securityToken' => $config->securityToken,
]);
} else {
$innerProvider = new StaticAKCredentialsProvider([
'accessKeyId' => $config->accessKeyId,
'accessKeySecret' => $config->accessKeySecret,
]);
}
$provider = new RamRoleArnCredentialsProvider([
'credentialsProvider' => $innerProvider,
'roleArn' => $config->roleArn,
'roleSessionName' => $config->roleSessionName,
'policy' => $config->policy,
'durationSeconds' => $config->roleSessionExpiration,
'externalId' => $config->externalId,
'stsEndpoint' => $config->STSEndpoint,
], [
'connectTimeout' => $config->connectTimeout,
'readTimeout' => $config->readTimeout,
]);
return new CredentialsProviderWrap('ram_role_arn', $provider);
case 'rsa_key_pair':
$provider = new RsaKeyPairCredentialsProvider([
'publicKeyId' => $config->publicKeyId,
'privateKeyFile' => $config->privateKeyFile,
'durationSeconds' => $config->roleSessionExpiration,
'stsEndpoint' => $config->STSEndpoint,
], [
'connectTimeout' => $config->connectTimeout,
'readTimeout' => $config->readTimeout,
]);
return new CredentialsProviderWrap('rsa_key_pair', $provider);
case 'ecs_ram_role':
$provider = new EcsRamRoleCredentialsProvider([
'roleName' => $config->roleName,
'disableIMDSv1' => $config->disableIMDSv1,
], [
'connectTimeout' => $config->connectTimeout,
'readTimeout' => $config->readTimeout,
]);
return new CredentialsProviderWrap('ecs_ram_role', $provider);
case 'oidc_role_arn':
$provider = new OIDCRoleArnCredentialsProvider([
'roleArn' => $config->roleArn,
'oidcProviderArn' => $config->oidcProviderArn,
'oidcTokenFilePath' => $config->oidcTokenFilePath,
'roleSessionName' => $config->roleSessionName,
'policy' => $config->policy,
'durationSeconds' => $config->roleSessionExpiration,
'stsEndpoint' => $config->STSEndpoint,
], [
'connectTimeout' => $config->connectTimeout,
'readTimeout' => $config->readTimeout,
]);
return new CredentialsProviderWrap('oidc_role_arn', $provider);
case "credentials_uri":
$provider = new URLCredentialsProvider([
'credentialsURI' => $config->credentialsURI,
], [
'connectTimeout' => $config->connectTimeout,
'readTimeout' => $config->readTimeout,
]);
return new CredentialsProviderWrap('credentials_uri', $provider);
default:
throw new InvalidArgumentException('Unsupported credential type option: ' . $config->type . ', support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri');
}
}
/**
* @return CredentialModel
* @throws RuntimeException
* @throws GuzzleException
*/
public function getCredential()
{
return $this->credential->getCredential();
}
/**
* @return array
*/
public function getConfig()
{
return $this->config->toMap();
}
/**
* @deprecated use getCredential() instead
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
public function getType()
{
return $this->credential->getCredential()->getType();
}
/**
* @deprecated use getCredential() instead
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
public function getAccessKeyId()
{
return $this->credential->getCredential()->getAccessKeyId();
}
/**
* @deprecated use getCredential() instead
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
public function getAccessKeySecret()
{
return $this->credential->getCredential()->getAccessKeySecret();
}
/**
* @deprecated use getCredential() instead
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
public function getSecurityToken()
{
return $this->credential->getCredential()->getSecurityToken();
}
/**
* @deprecated use getCredential() instead
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
public function getBearerToken()
{
return $this->credential->getCredential()->getBearerToken();
}
/**
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call($name, $arguments)
{
return $this->credential->$name($arguments);
}
}
@@ -0,0 +1,270 @@
<?php
namespace AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Tea\Model;
class Config extends Model
{
public function validate()
{
}
public function toMap()
{
$res = [];
if (null !== $this->accessKeyId) {
$res['accessKeyId'] = $this->accessKeyId;
}
if (null !== $this->accessKeySecret) {
$res['accessKeySecret'] = $this->accessKeySecret;
}
if (null !== $this->securityToken) {
$res['securityToken'] = $this->securityToken;
}
if (null !== $this->bearerToken) {
$res['bearerToken'] = $this->bearerToken;
}
if (null !== $this->durationSeconds) {
$res['durationSeconds'] = $this->durationSeconds;
}
if (null !== $this->roleArn) {
$res['roleArn'] = $this->roleArn;
}
if (null !== $this->policy) {
$res['policy'] = $this->policy;
}
if (null !== $this->roleSessionExpiration) {
$res['roleSessionExpiration'] = $this->roleSessionExpiration;
}
if (null !== $this->roleSessionName) {
$res['roleSessionName'] = $this->roleSessionName;
}
if (null !== $this->publicKeyId) {
$res['publicKeyId'] = $this->publicKeyId;
}
if (null !== $this->privateKeyFile) {
$res['privateKeyFile'] = $this->privateKeyFile;
}
if (null !== $this->roleName) {
$res['roleName'] = $this->roleName;
}
if (null !== $this->credentialsURI) {
$res['credentialsURI'] = $this->credentialsURI;
}
if (null !== $this->type) {
$res['type'] = $this->type;
}
if (null !== $this->STSEndpoint) {
$res['STSEndpoint'] = $this->STSEndpoint;
}
if (null !== $this->externalId) {
$res['externalId'] = $this->externalId;
}
return $res;
}
/**
* @param array $map
* @return Config
*/
public static function fromMap($map = [])
{
$model = new self();
if (isset($map['accessKeyId'])) {
$model->accessKeyId = $map['accessKeyId'];
}
if (isset($map['accessKeySecret'])) {
$model->accessKeySecret = $map['accessKeySecret'];
}
if (isset($map['securityToken'])) {
$model->securityToken = $map['securityToken'];
}
if (isset($map['bearerToken'])) {
$model->bearerToken = $map['bearerToken'];
}
if (isset($map['durationSeconds'])) {
$model->durationSeconds = $map['durationSeconds'];
}
if (isset($map['roleArn'])) {
$model->roleArn = $map['roleArn'];
}
if (isset($map['policy'])) {
$model->policy = $map['policy'];
}
if (isset($map['roleSessionExpiration'])) {
$model->roleSessionExpiration = $map['roleSessionExpiration'];
}
if (isset($map['roleSessionName'])) {
$model->roleSessionName = $map['roleSessionName'];
}
if (isset($map['publicKeyId'])) {
$model->publicKeyId = $map['publicKeyId'];
}
if (isset($map['privateKeyFile'])) {
$model->privateKeyFile = $map['privateKeyFile'];
}
if (isset($map['roleName'])) {
$model->roleName = $map['roleName'];
}
if (isset($map['credentialsURI'])) {
$model->credentialsURI = $map['credentialsURI'];
}
if (isset($map['type'])) {
$model->type = $map['type'];
}
if (isset($map['STSEndpoint'])) {
$model->STSEndpoint = $map['STSEndpoint'];
}
if (isset($map['externalId'])) {
$model->externalId = $map['externalId'];
}
return $model;
}
/**
* @description credential type
* @example access_key
* @var string
*/
public $type = 'default';
/**
* @description accesskey id
* @var string
*/
public $accessKeyId;
/**
* @description accesskey secret
* @var string
*/
public $accessKeySecret;
/**
* @description security token
* @var string
*/
public $securityToken;
/**
* @description bearer token
* @var string
*/
public $bearerToken;
/**
* @description role name
* @var string
*/
public $roleName;
/**
* @description role arn
* @var string
*/
public $roleArn;
/**
* @description oidc provider arn
* @var string
*/
public $oidcProviderArn;
/**
* @description oidc token file path
* @var string
*/
public $oidcTokenFilePath;
/**
* @description role session expiration
* @example 3600
* @var int
*/
public $roleSessionExpiration;
/**
* @description role session name
* @var string
*/
public $roleSessionName;
/**
* @description role arn policy
* @var string
*/
public $policy;
/**
* @description external id for ram role arn
* @var string
*/
public $externalId;
/**
* @description sts endpoint
* @var string
*/
public $STSEndpoint;
public $publicKeyId;
public $privateKeyFile;
/**
* @description read timeout
* @var int
*/
public $readTimeout;
/**
* @description connection timeout
* @var int
*/
public $connectTimeout;
/**
* @description disable IMDS v1
* @var bool
*/
public $disableIMDSv1;
/**
* @description credentials URI
* @var string
*/
public $credentialsURI;
/**
* @deprecated
*/
public $metadataTokenDuration;
/**
* @deprecated
*/
public $durationSeconds;
/**
* @deprecated
*/
public $host;
/**
* @deprecated
*/
public $expiration;
/**
* @deprecated
*/
public $certFile = "";
/**
* @deprecated
*/
public $certPassword = "";
/**
* @internal
*/
public $proxy;
}
@@ -0,0 +1,143 @@
<?php
// This file is auto-generated, don't edit it. Thanks.
namespace AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Tea\Model;
class CredentialModel extends Model
{
public function validate()
{
}
public function toMap()
{
$res = [];
if (null !== $this->accessKeyId) {
$res['accessKeyId'] = $this->accessKeyId;
}
if (null !== $this->accessKeySecret) {
$res['accessKeySecret'] = $this->accessKeySecret;
}
if (null !== $this->securityToken) {
$res['securityToken'] = $this->securityToken;
}
if (null !== $this->bearerToken) {
$res['bearerToken'] = $this->bearerToken;
}
if (null !== $this->type) {
$res['type'] = $this->type;
}
if (null !== $this->providerName) {
$res['providerName'] = $this->providerName;
}
return $res;
}
/**
* @param array $map
* @return CredentialModel
*/
public static function fromMap($map = [])
{
$model = new self();
if (isset($map['accessKeyId'])) {
$model->accessKeyId = $map['accessKeyId'];
}
if (isset($map['accessKeySecret'])) {
$model->accessKeySecret = $map['accessKeySecret'];
}
if (isset($map['securityToken'])) {
$model->securityToken = $map['securityToken'];
}
if (isset($map['bearerToken'])) {
$model->bearerToken = $map['bearerToken'];
}
if (isset($map['type'])) {
$model->type = $map['type'];
}
if(isset($map['providerName'])){
$model->providerName = $map['providerName'];
}
return $model;
}
/**
* @description accesskey id
* @var string
*/
public $accessKeyId;
/**
* @description accesskey secret
* @var string
*/
public $accessKeySecret;
/**
* @description security token
* @var string
*/
public $securityToken;
/**
* @description bearer token
* @var string
*/
public $bearerToken;
/**
* @description type
* @example access_key
* @var string
*/
public $type;
/**
* @description provider name
* @example cli_profile/static_ak
* @var string
*/
public $providerName;
/**
* @return string
*/
public function getAccessKeyId()
{
return $this->accessKeyId;
}
/**
* @return string
*/
public function getAccessKeySecret()
{
return $this->accessKeySecret;
}
/**
* @return string
*/
public function getSecurityToken()
{
return $this->securityToken;
}
/**
* @return string
*/
public function getBearerToken()
{
return $this->bearerToken;
}
public function getType()
{
return $this->type;
}
public function getProviderName()
{
return $this->providerName;
}
}
@@ -0,0 +1,97 @@
<?php
namespace AlibabaCloud\Credentials\Credential;
use AlibabaCloud\Credentials\Providers\Credentials;
class RefreshResult
{
/**
* RefreshResult constructor.
* @param Credentials $params
* @param int $staleTime
* @param int $prefetchTime
*/
public function __construct($credentials = null, $staleTime = PHP_INT_MAX, $prefetchTime = PHP_INT_MAX)
{
$this->credentials = $credentials;
$this->staleTime = $staleTime;
$this->prefetchTime = $prefetchTime;
}
public function validate() {}
public function toMap()
{
$res = [];
if (null !== $this->staleTime) {
$res['staleTime'] = $this->staleTime;
}
if (null !== $this->prefetchTime) {
$res['prefetchTime'] = $this->prefetchTime;
}
if (null !== $this->credentials) {
$res['credentials'] = $this->credentials;
}
return $res;
}
/**
* @param array $map
* @return RefreshResult
*/
public static function fromMap($map = [])
{
$model = new self();
if (isset($map['staleTime'])) {
$model->staleTime = $map['staleTime'];
}
if (isset($map['prefetchTime'])) {
$model->staleTime = $map['prefetchTime'];
}
if (isset($map['credentials'])) {
$model->staleTime = $map['credentials'];
}
return $model;
}
/**
* @description staleTime
* @var int
*/
public $staleTime;
/**
* @description prefetchTime
* @var int
*/
public $prefetchTime;
/**
* @description credentials
* @var Credentials
*/
public $credentials;
/**
* @return Credentials
*/
public function credentials()
{
return $this->credentials;
}
/**
* @var int
*/
public function staleTime()
{
return $this->staleTime;
}
/**
* @var int
*/
public function prefetchTime()
{
return $this->prefetchTime;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Providers\ChainProvider;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Utils\MockTrait;
use ReflectionException;
use RuntimeException;
/**
* Class Credentials
*
* @package AlibabaCloud\Credentials
*/
class Credentials
{
use MockTrait;
/**
* @var array|CredentialsInterface[] containers of credentials
*/
protected static $credentials = [];
/**
* Get the credential instance by name.
*
* @param string $name
*
* @return Credential
* @throws ReflectionException
*/
public static function get($name = null)
{
if ($name !== null) {
Filter::credentialName($name);
} else {
$name = ChainProvider::getDefaultName();
}
self::load();
if (self::has($name)) {
return new Credential(self::$credentials[\strtolower($name)]);
}
throw new RuntimeException("Credential '$name' not found");
}
private static function load()
{
if (self::$credentials) {
return;
}
if (ChainProvider::hasCustomChain()) {
ChainProvider::customProvider(ChainProvider::getDefaultName());
} else {
ChainProvider::defaultProvider(ChainProvider::getDefaultName());
}
}
/**
* Determine whether there is a credential.
*
* @param string $name
*
* @return bool
*/
public static function has($name)
{
Filter::credentialName($name);
return isset(self::$credentials[\strtolower($name)]);
}
public static function flush()
{
self::$credentials = [];
}
/**
* Get all credentials.
*
* @return array
*/
public static function all()
{
self::load();
return self::$credentials;
}
/**
* @param string $name
* @param array $credential
*/
public static function set($name, array $credential)
{
Filter::credentialName($name);
self::$credentials[\strtolower($name)] = \array_change_key_case($credential);
}
}
@@ -0,0 +1,32 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\SignatureInterface;
/**
* @internal This class is intended for internal use within the package.
* Interface CredentialsInterface
*
* @codeCoverageIgnore
*/
interface CredentialsInterface
{
/**
* @deprecated
* @return string
*/
public function __toString();
/**
* @deprecated
* @return SignatureInterface
*/
public function getSignature();
/**
* @return CredentialModel
*/
public function getCredential();
}
@@ -0,0 +1,76 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Providers\CredentialsProvider;
/**
* @internal This class is intended for internal use within the package.
* Class CredentialsProviderWrap
*
* @package AlibabaCloud\Credentials
*/
class CredentialsProviderWrap implements CredentialsInterface
{
/**
* @var string
*/
private $typeName;
/**
* @var CredentialsProvider
*/
private $credentialsProvider;
/**
* CLIProfileCredentialsProvider constructor.
*
* @param string $typeName
* @param CredentialsProvider $credentialsProvider
*/
public function __construct($typeName, $credentialsProvider)
{
$this->typeName = $typeName;
$this->credentialsProvider = $credentialsProvider;
}
/**
* @inheritDoc
*/
public function getCredential()
{
$credentials = $this->credentialsProvider->getCredentials();
return new CredentialModel([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'type' => $this->typeName,
'providerName' => $credentials->getProviderName(),
]);
}
/**
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call($name, $arguments)
{
return $this->credentialsProvider->$name($arguments);
}
public function __toString()
{
return "credentialsProviderWrap#$this->typeName";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return null;
}
}
@@ -0,0 +1,199 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\ShaHmac1Signature;
use AlibabaCloud\Credentials\Request\Request;
use AlibabaCloud\Credentials\Utils\Filter;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
/**
* @deprecated
* Use the RAM role of an ECS instance to complete the authentication.
*/
class EcsRamRoleCredential implements CredentialsInterface
{
/**
* @var string
*/
private $roleName;
/**
* @var boolean
*/
private $disableIMDSv1;
/**
* @var int
*/
private $metadataTokenDuration;
/**
* EcsRamRoleCredential constructor.
*
* @param $role_name
*/
public function __construct($role_name = null, $disable_imdsv1 = false, $metadata_token_duration = 21600)
{
Filter::roleName($role_name);
$this->roleName = $role_name;
Filter::disableIMDSv1($disable_imdsv1);
$this->disableIMDSv1 = $disable_imdsv1;
$this->metadataTokenDuration = $metadata_token_duration;
}
/**
* @return string
* @throws GuzzleException
* @throws Exception
*/
public function getRoleName()
{
if ($this->roleName !== null) {
return $this->roleName;
}
$this->roleName = $this->getRoleNameFromMeta();
return $this->roleName;
}
/**
* @return string
* @throws Exception
*/
public function getRoleNameFromMeta()
{
$options = [
'http_errors' => false,
'timeout' => 1,
'connect_timeout' => 1,
];
$result = Request::createClient()->request(
'GET',
'http://100.100.100.200/latest/meta-data/ram/security-credentials/',
$options
);
if ($result->getStatusCode() === 404) {
throw new InvalidArgumentException('The role name was not found in the instance');
}
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error retrieving credentials from result: ' . $result->getBody());
}
$role_name = (string) $result;
if (!$role_name) {
throw new RuntimeException('Error retrieving credentials from result is empty');
}
return $role_name;
}
/**
* @return string
*/
public function __toString()
{
return "roleName#$this->roleName";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return new ShaHmac1Signature();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeyId()
{
return $this->getSessionCredential()->getAccessKeyId();
}
/**
* @return AlibabaCloud\Credentials\Providers\Credentials
* @throws Exception
* @throws GuzzleException
*/
protected function getSessionCredential()
{
$params = [
"roleName" => $this->roleName,
'disableIMDSv1' => $this->disableIMDSv1,
'metadataTokenDuration' => $this->metadataTokenDuration,
];
return (new EcsRamRoleCredentialsProvider($params))->getCredentials();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeySecret()
{
return $this->getSessionCredential()->getAccessKeySecret();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getSecurityToken()
{
return $this->getSessionCredential()->getSecurityToken();
}
/**
* @return int
* @throws Exception
* @throws GuzzleException
*/
public function getExpiration()
{
return $this->getSessionCredential()->getExpiration();
}
/**
* @return bool
*/
public function isDisableIMDSv1()
{
return $this->disableIMDSv1;
}
/**
* @inheritDoc
*/
public function getCredential()
{
$credentials = $this->getSessionCredential();
return new CredentialModel([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'type' => 'ecs_ram_role',
]);
}
}
@@ -0,0 +1,398 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use RuntimeException;
/**
* @internal This class is intended for internal use within the package.
* Class CLIProfileCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class CLIProfileCredentialsProvider implements CredentialsProvider
{
private static $oauthBaseUrlMap = [
'CN' => 'https://oauth.aliyun.com',
'INTL' => 'https://oauth.alibabacloud.com',
];
private static $oauthClientMap = [
'CN' => '4038181954557748008',
'INTL' => '4103531455503354461',
];
/**
* @var string
*/
private $profileName;
/**
* @var CredentialsProvider
*/
private $credentialsProvider;
/**
* CLIProfileCredentialsProvider constructor.
*
* @param array $params
*/
public function __construct(array $params = [])
{
$this->filterProfileName($params);
}
private function filterProfileName(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_PROFILE')) {
$this->profileName = Helper::env('ALIBABA_CLOUD_PROFILE');
}
if (isset($params['profileName'])) {
$this->profileName = $params['profileName'];
}
}
/**
* @return bool
*/
private function shouldReloadCredentialsProvider()
{
if (is_null($this->credentialsProvider)) {
return true;
}
return false;
}
/**
* @return CredentialsProvider
*/
protected function reloadCredentialsProvider($profileFile, $profileName)
{
if (!Helper::inOpenBasedir($profileFile)) {
throw new RuntimeException('Unable to open credentials file: ' . $profileFile);
}
if (!\is_readable($profileFile) || !\is_file($profileFile)) {
throw new RuntimeException('Credentials file is not readable: ' . $profileFile);
}
$jsonContent = \file_get_contents($profileFile);
$fileArray = json_decode($jsonContent, true);
if (\is_array($fileArray) && !empty($fileArray)) {
if (is_null($profileName) || $profileName === '') {
$profileName = $fileArray['current'];
}
if (isset($fileArray['profiles'])) {
foreach ($fileArray['profiles'] as $profile) {
if (Helper::unsetReturnNull($profile, 'name') === $profileName) {
switch (Helper::unsetReturnNull($profile, 'mode')) {
case 'AK':
return new StaticAKCredentialsProvider([
'accessKeyId' => Helper::unsetReturnNull($profile, 'access_key_id'),
'accessKeySecret' => Helper::unsetReturnNull($profile, 'access_key_secret'),
]);
case 'StsToken':
return new StaticSTSCredentialsProvider([
'accessKeyId' => Helper::unsetReturnNull($profile, 'access_key_id'),
'accessKeySecret' => Helper::unsetReturnNull($profile, 'access_key_secret'),
'securityToken' => Helper::unsetReturnNull($profile, 'sts_token'),
]);
case 'RamRoleArn':
$innerProvider = new StaticAKCredentialsProvider([
'accessKeyId' => Helper::unsetReturnNull($profile, 'access_key_id'),
'accessKeySecret' => Helper::unsetReturnNull($profile, 'access_key_secret'),
]);
return new RamRoleArnCredentialsProvider([
'credentialsProvider' => $innerProvider,
'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'),
'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'),
'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'),
'policy' => Helper::unsetReturnNull($profile, 'policy'),
'externalId' => Helper::unsetReturnNull($profile, 'external_id'),
'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'),
'enableVpc' => Helper::unsetReturnNull($profile, 'enable_vpc'),
]);
case 'EcsRamRole':
return new EcsRamRoleCredentialsProvider([
'roleName' => Helper::unsetReturnNull($profile, 'ram_role_name'),
]);
case 'OIDC':
return new OIDCRoleArnCredentialsProvider([
'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'),
'oidcProviderArn' => Helper::unsetReturnNull($profile, 'oidc_provider_arn'),
'oidcTokenFilePath' => Helper::unsetReturnNull($profile, 'oidc_token_file'),
'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'),
'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'),
'policy' => Helper::unsetReturnNull($profile, 'policy'),
'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'),
'enableVpc' => Helper::unsetReturnNull($profile, 'enable_vpc'),
]);
case 'ChainableRamRoleArn':
$previousProvider = $this->reloadCredentialsProvider($profileFile, Helper::unsetReturnNull($profile, 'source_profile'));
return new RamRoleArnCredentialsProvider([
'credentialsProvider' => $previousProvider,
'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'),
'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'),
'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'),
'policy' => Helper::unsetReturnNull($profile, 'policy'),
'externalId' => Helper::unsetReturnNull($profile, 'external_id'),
'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'),
'enableVpc' => Helper::unsetReturnNull($profile, 'enable_vpc'),
]);
case 'CloudSSO':
return new CloudSSOCredentialsProvider([
'signInUrl' => Helper::unsetReturnNull($profile, 'cloud_sso_sign_in_url'),
'accountId' => Helper::unsetReturnNull($profile, 'cloud_sso_account_id'),
'accessConfig' => Helper::unsetReturnNull($profile, 'cloud_sso_access_config'),
'accessToken' => Helper::unsetReturnNull($profile, 'access_token'),
'accessTokenExpire' => Helper::unsetReturnNull($profile, 'cloud_sso_access_token_expire'),
]);
case 'OAuth':
$siteType = strtoupper(Helper::unsetReturnNull($profile, 'oauth_site_type') ?: '');
if (!isset(self::$oauthBaseUrlMap[$siteType])) {
throw new RuntimeException('Invalid OAuth site type, support CN or INTL.');
}
$oauthSignInUrl = self::$oauthBaseUrlMap[$siteType];
$oauthClientId = self::$oauthClientMap[$siteType];
return new OAuthCredentialsProvider([
'signInUrl' => $oauthSignInUrl,
'clientId' => $oauthClientId,
'refreshToken' => Helper::unsetReturnNull($profile, 'oauth_refresh_token'),
'accessToken' => Helper::unsetReturnNull($profile, 'oauth_access_token'),
'accessTokenExpire' => Helper::unsetReturnNull($profile, 'oauth_access_token_expire'),
'tokenUpdateCallback' => $this->createOAuthTokenUpdateCallback($profileFile, $profileName),
]);
case 'External':
return new ExternalCredentialsProvider([
'processCommand' => (string) Helper::unsetReturnNull($profile, 'process_command'),
'credentialUpdateCallback' => $this->createExternalCredentialUpdateCallback($profileFile, $profileName),
]);
default:
throw new RuntimeException('Unsupported credential mode from CLI credentials file: ' . (string) Helper::unsetReturnNull($profile, 'mode'));
}
}
}
}
}
throw new RuntimeException('Failed to get credential from CLI credentials file: ' . $profileFile);
}
/**
* Get credential.
*
* @return Credentials
* @throws RuntimeException
*/
public function getCredentials()
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_CLI_PROFILE_DISABLED') && Helper::env('ALIBABA_CLOUD_CLI_PROFILE_DISABLED') === true) {
throw new RuntimeException('CLI credentials file is disabled');
}
$cliProfileFile = self::getDefaultFile();
if ($this->shouldReloadCredentialsProvider()) {
$this->credentialsProvider = $this->reloadCredentialsProvider($cliProfileFile, $this->profileName);
}
$credentials = $this->credentialsProvider->getCredentials();
return new Credentials([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'providerName' => $this->getProviderName() . '/' . $this->credentialsProvider->getProviderName(),
]);
}
/**
* @param string $profileFile
* @param string $profileName
* @return callable
*/
private function createOAuthTokenUpdateCallback($profileFile, $profileName)
{
return function ($refreshToken, $accessToken, $accessKeyId, $accessKeySecret, $securityToken, $accessTokenExpire, $stsExpire) use ($profileFile, $profileName) {
try {
$this->updateOAuthTokens($profileFile, $profileName, $refreshToken, $accessToken, $accessKeyId, $accessKeySecret, $securityToken, $accessTokenExpire, $stsExpire);
} catch (\Exception $e) {
// Warning only
}
};
}
/**
* @param string $profileFile
* @param string $profileName
* @return callable
*/
private function createExternalCredentialUpdateCallback($profileFile, $profileName)
{
return function ($accessKeyId, $accessKeySecret, $securityToken, $expiration) use ($profileFile, $profileName) {
try {
$this->updateExternalCredentials($profileFile, $profileName, $accessKeyId, $accessKeySecret, $securityToken, $expiration);
} catch (\Exception $e) {
// Warning only
}
};
}
/**
* @param string $profileFile
* @param string $profileName
* @param string $refreshToken
* @param string $accessToken
* @param string $accessKeyId
* @param string $accessKeySecret
* @param string $securityToken
* @param int $accessTokenExpire
* @param int $stsExpire
*/
private function updateOAuthTokens($profileFile, $profileName, $refreshToken, $accessToken, $accessKeyId, $accessKeySecret, $securityToken, $accessTokenExpire, $stsExpire)
{
if (!file_exists($profileFile)) {
return;
}
$jsonContent = file_get_contents($profileFile);
$config = json_decode($jsonContent, true);
if (empty($config) || !isset($config['profiles'])) {
return;
}
$oauthProfile = $this->findOAuthProfile($config, $profileName);
if ($oauthProfile === null) {
return;
}
$oauthProfile['oauth_refresh_token'] = $refreshToken;
$oauthProfile['oauth_access_token'] = $accessToken;
$oauthProfile['oauth_access_token_expire'] = $accessTokenExpire;
$oauthProfile['access_key_id'] = $accessKeyId;
$oauthProfile['access_key_secret'] = $accessKeySecret;
$oauthProfile['sts_token'] = $securityToken;
$oauthProfile['sts_expiration'] = $stsExpire;
foreach ($config['profiles'] as &$p) {
if (isset($p['name']) && $p['name'] === $oauthProfile['name']) {
$p = $oauthProfile;
break;
}
}
unset($p);
file_put_contents($profileFile, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
/**
* @param string $profileFile
* @param string $profileName
* @param string $accessKeyId
* @param string $accessKeySecret
* @param string $securityToken
* @param int $expiration
*/
private function updateExternalCredentials($profileFile, $profileName, $accessKeyId, $accessKeySecret, $securityToken, $expiration)
{
if (!file_exists($profileFile)) {
return;
}
$jsonContent = file_get_contents($profileFile);
$config = json_decode($jsonContent, true);
if (empty($config) || !isset($config['profiles'])) {
return;
}
$externalProfile = $this->findExternalProfile($config, $profileName);
if ($externalProfile === null) {
return;
}
$externalProfile['access_key_id'] = $accessKeyId;
$externalProfile['access_key_secret'] = $accessKeySecret;
$externalProfile['sts_token'] = $securityToken;
$externalProfile['sts_expiration'] = $expiration;
foreach ($config['profiles'] as &$p) {
if (isset($p['name']) && $p['name'] === $externalProfile['name']) {
$p = $externalProfile;
break;
}
}
unset($p);
file_put_contents($profileFile, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
/**
* @param array $config
* @param string $profileName
* @return array|null
*/
private function findOAuthProfile($config, $profileName)
{
if (!isset($config['profiles'])) {
return null;
}
foreach ($config['profiles'] as $p) {
if (isset($p['name']) && $p['name'] === $profileName) {
if (isset($p['mode']) && $p['mode'] === 'OAuth') {
return $p;
}
if (!empty($p['source_profile'])) {
return $this->findOAuthProfile($config, $p['source_profile']);
}
return null;
}
}
return null;
}
/**
* @param array $config
* @param string $profileName
* @return array|null
*/
private function findExternalProfile($config, $profileName)
{
if (!isset($config['profiles'])) {
return null;
}
foreach ($config['profiles'] as $p) {
if (isset($p['name']) && $p['name'] === $profileName) {
if (isset($p['mode']) && $p['mode'] === 'External') {
return $p;
}
if (!empty($p['source_profile'])) {
return $this->findExternalProfile($config, $p['source_profile']);
}
return null;
}
}
return null;
}
/**
* Get the default credential file.
*
* @return string
*/
private function getDefaultFile()
{
return Helper::getHomeDirectory() .
DIRECTORY_SEPARATOR .
'.aliyun' .
DIRECTORY_SEPARATOR .
'config.json';
}
/**
* @return string
*/
public function getProviderName()
{
return 'cli_profile';
}
}
@@ -0,0 +1,188 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Credentials;
use AlibabaCloud\Credentials\Utils\Helper;
use Closure;
use InvalidArgumentException;
use RuntimeException;
/**
* @deprecated
* Class ChainProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class ChainProvider
{
/**
* @var array
*/
private static $customChains;
/**
* @param callable ...$providers
*/
public static function set(...$providers)
{
if (empty($providers)) {
throw new InvalidArgumentException('No providers in chain');
}
foreach ($providers as $provider) {
if (!$provider instanceof Closure) {
throw new InvalidArgumentException('Providers must all be Closures');
}
}
self::$customChains = $providers;
}
/**
* @return bool
*/
public static function hasCustomChain()
{
return (bool)self::$customChains;
}
public static function flush()
{
self::$customChains = [];
}
/**
* @param string $name
*/
public static function customProvider($name)
{
foreach (self::$customChains as $provider) {
$provider();
if (Credentials::has($name)) {
break;
}
}
}
/**
* @param string $name
*/
public static function defaultProvider($name)
{
$providers = [
self::env(),
self::ini(),
self::instance(),
];
foreach ($providers as $provider) {
$provider();
if (Credentials::has($name)) {
break;
}
}
}
/**
* @return Closure
*/
public static function env()
{
return static function () {
$accessKeyId = Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID');
$accessKeySecret = Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET');
if ($accessKeyId && $accessKeySecret) {
Credentials::set(
self::getDefaultName(),
[
'type' => 'access_key',
'access_key_id' => $accessKeyId,
'access_key_secret' => $accessKeySecret,
]
);
}
};
}
/**
* @return string
*/
public static function getDefaultName()
{
$name = Helper::envNotEmpty('ALIBABA_CLOUD_PROFILE');
if ($name) {
return $name;
}
return 'default';
}
/**
* @return Closure
*/
public static function ini()
{
return static function () {
$filename = Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_FILE');
if (!$filename) {
$filename = self::getDefaultFile();
}
if (!Helper::inOpenBasedir($filename)) {
return;
}
if ($filename !== self::getDefaultFile() && (!\is_readable($filename) || !\is_file($filename))) {
throw new RuntimeException(
'Credentials file is not readable: ' . $filename
);
}
$file_array = \parse_ini_file($filename, true);
if (\is_array($file_array) && !empty($file_array)) {
foreach (\array_change_key_case($file_array) as $name => $configures) {
Credentials::set($name, $configures);
}
}
};
}
/**
* Get the default credential file.
*
* @return string
*/
public static function getDefaultFile()
{
return Helper::getHomeDirectory() .
DIRECTORY_SEPARATOR .
'.alibabacloud' .
DIRECTORY_SEPARATOR .
'credentials';
}
/**
* @return Closure
*/
public static function instance()
{
return static function () {
$instance = Helper::envNotEmpty('ALIBABA_CLOUD_ECS_METADATA');
if ($instance) {
Credentials::set(
self::getDefaultName(),
[
'type' => 'ecs_ram_role',
'role_name' => $instance,
]
);
}
};
}
}
@@ -0,0 +1,162 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Request\Request;
use AlibabaCloud\Credentials\Credential\RefreshResult;
use GuzzleHttp\Psr7\Uri;
use InvalidArgumentException;
use RuntimeException;
/**
* @internal This class is intended for internal use within the package.
* Class CloudSSOCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class CloudSSOCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $signInUrl;
/**
* @var string
*/
private $accountId;
/**
* @var string
*/
private $accessConfig;
/**
* @var string
*/
private $accessToken;
/**
* @var int
*/
private $accessTokenExpire;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 10;
/**
* CloudSSOCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterParams($params);
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
}
private function filterParams(array $params)
{
$this->signInUrl = isset($params['signInUrl']) ? $params['signInUrl'] : null;
$this->accountId = isset($params['accountId']) ? $params['accountId'] : null;
$this->accessConfig = isset($params['accessConfig']) ? $params['accessConfig'] : null;
$this->accessToken = isset($params['accessToken']) ? $params['accessToken'] : null;
$this->accessTokenExpire = isset($params['accessTokenExpire']) ? (int) $params['accessTokenExpire'] : 0;
if (empty($this->accessToken) || $this->accessTokenExpire === 0
|| $this->accessTokenExpire - time() <= 0) {
throw new InvalidArgumentException('CloudSSO access token is empty or expired, please re-login with cli.');
}
if (empty($this->signInUrl) || empty($this->accountId) || empty($this->accessConfig)) {
throw new InvalidArgumentException('CloudSSO sign in url, account id, and access config cannot be empty.');
}
}
/**
* @return RefreshResult
* @throws RuntimeException
*/
public function refreshCredentials()
{
$parsedUrl = parse_url($this->signInUrl);
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] : 'https';
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
$requestUrl = $scheme . '://' . $host . '/cloud-credentials';
$body = json_encode([
'AccountId' => $this->accountId,
'AccessConfigurationId' => $this->accessConfig,
]);
$options = [
'http_errors' => false,
'connect_timeout' => $this->connectTimeout,
'read_timeout' => $this->readTimeout,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->accessToken,
],
'body' => $body,
];
$result = Request::createClient()->request('POST', $requestUrl, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Get session token from CloudSSO failed, statusCode: '
. $result->getStatusCode() . ', result: ' . (string) $result->getBody());
}
$json = json_decode((string) $result->getBody(), true);
if (!isset($json['CloudCredential'])) {
throw new RuntimeException('Get session token from CloudSSO failed, fail to get credentials.');
}
$credentials = $json['CloudCredential'];
if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret'])
|| !isset($credentials['SecurityToken'])) {
throw new RuntimeException('Get session token from CloudSSO failed, fail to get credentials.');
}
$expiration = \strtotime($credentials['Expiration']);
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => $expiration,
'providerName' => $this->getProviderName(),
]), $this->getStaleTime($expiration));
}
public function key()
{
return 'cloud_sso#' . $this->signInUrl . '#' . $this->accountId . '#' . $this->accessConfig;
}
public function getProviderName()
{
return 'cloud_sso';
}
}
@@ -0,0 +1,87 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
/**
* @internal This class is intended for internal use within the package.
* Class Credentials
*
* @package AlibabaCloud\Credentials\Providers
*/
class Credentials
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* @var string
*/
private $securityToken;
/**
* @var int
*/
private $expiration;
/**
* @var int
*/
private $providerName;
public function __construct($config = [])
{
if (!empty($config)) {
foreach ($config as $k => $v) {
$this->{$k} = $v;
}
}
}
/**
* @return string
*/
public function getAccessKeyId()
{
return $this->accessKeyId;
}
/**
* @return string
*/
public function getAccessKeySecret()
{
return $this->accessKeySecret;
}
/**
* @return string
*/
public function getSecurityToken()
{
return $this->securityToken;
}
/**
* @return int
*/
public function getExpiration()
{
return $this->expiration;
}
/**
* @return string
*/
public function getProviderName()
{
return $this->providerName;
}
}
@@ -0,0 +1,24 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
/**
* @internal This class is intended for internal use within the package.
* Interface CredentialsInterface
*
* @codeCoverageIgnore
*/
interface CredentialsProvider
{
/**
* @return Credentials
*/
public function getCredentials();
/**
* @return string
*/
public function getProviderName();
}
@@ -0,0 +1,175 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Utils\Helper;
use InvalidArgumentException;
use RuntimeException;
use Exception;
/**
* @internal This class is intended for internal use within the package.
* Class DefaultCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class DefaultCredentialsProvider implements CredentialsProvider
{
/**
* @var array
*/
private static $defaultProviders = [];
/**
* @var bool
*/
private $reuseLastProviderEnabled;
/**
* @var CredentialsProvider
*/
private $lastUsedCredentialsProvider;
/**
* @var array
*/
private static $customChain = [];
/**
* DefaultCredentialsProvider constructor.
* @param array $params
*/
public function __construct(array $params = [])
{
$this->filterReuseLastProviderEnabled($params);
$this->createDefaultChain();
Filter::reuseLastProviderEnabled($this->reuseLastProviderEnabled);
}
private function filterReuseLastProviderEnabled(array $params)
{
$this->reuseLastProviderEnabled = true;
if (isset($params['reuseLastProviderEnabled'])) {
$this->reuseLastProviderEnabled = $params['reuseLastProviderEnabled'];
}
}
private function createDefaultChain()
{
self::$defaultProviders = [
new EnvironmentVariableCredentialsProvider(),
];
if (
Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN')
&& Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_PROVIDER_ARN')
&& Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_TOKEN_FILE')
) {
array_push(
self::$defaultProviders,
new OIDCRoleArnCredentialsProvider()
);
}
array_push(
self::$defaultProviders,
new CLIProfileCredentialsProvider()
);
array_push(
self::$defaultProviders,
new ProfileCredentialsProvider()
);
array_push(
self::$defaultProviders,
new EcsRamRoleCredentialsProvider()
);
if (Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_URI')) {
array_push(
self::$defaultProviders,
new URLCredentialsProvider()
);
}
}
/**
* @param CredentialsProvider ...$providers
*/
public static function set(...$providers)
{
if (empty($providers)) {
throw new InvalidArgumentException('No providers in chain');
}
foreach ($providers as $provider) {
if (!$provider instanceof CredentialsProvider) {
throw new InvalidArgumentException('Providers must all be CredentialsProvider');
}
}
self::$customChain = $providers;
}
/**
* @return bool
*/
public static function hasCustomChain()
{
return (bool) self::$customChain;
}
public static function flush()
{
self::$customChain = [];
}
/**
* Get credential.
*
* @return Credentials
* @throws RuntimeException
*/
public function getCredentials()
{
if ($this->reuseLastProviderEnabled && !is_null($this->lastUsedCredentialsProvider)) {
$credentials = $this->lastUsedCredentialsProvider->getCredentials();
return new Credentials([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'providerName' => $this->getProviderName() . '/' . $this->lastUsedCredentialsProvider->getProviderName(),
]);
}
$providerChain = array_merge(
self::$customChain,
self::$defaultProviders
);
$exceptionMessages = [];
foreach ($providerChain as $provider) {
try {
$credentials = $provider->getCredentials();
$this->lastUsedCredentialsProvider = $provider;
return new Credentials([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'providerName' => $this->getProviderName() . '/' . $provider->getProviderName(),
]);
} catch (Exception $exception) {
array_push($exceptionMessages, basename(str_replace('\\', '/', get_class($provider))) . ': ' . $exception->getMessage());
}
}
throw new RuntimeException('Unable to load credentials from any of the providers in the chain: ' . implode(', ', $exceptionMessages));
}
/**
* @inheritDoc
*/
public function getProviderName()
{
return "default";
}
}
@@ -0,0 +1,276 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Request\Request;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
use AlibabaCloud\Credentials\Credential\RefreshResult;
/**
* @internal This class is intended for internal use within the package.
* Class EcsRamRoleCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class EcsRamRoleCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $metadataHost = 'http://100.100.100.200';
/**
* @var string
*/
private $ecsUri = '/latest/meta-data/ram/security-credentials/';
/**
* @var string
*/
private $metadataTokenUri = '/latest/api/token';
/**
* @var string
*/
private $roleName;
/**
* @var boolean
*/
private $disableIMDSv1 = false;
/**
* @var int
*/
private $metadataTokenDuration = 21600;
/**
* @var int
*/
private $connectTimeout = 1;
/**
* @var int
*/
private $readTimeout = 1;
/**
* EcsRamRoleCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterRoleName($params);
$this->filterDisableECSIMDSv1($params);
Filter::roleName($this->roleName);
Filter::disableIMDSv1($this->disableIMDSv1);
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
Filter::timeout($this->connectTimeout, $this->readTimeout);
}
private function filterRoleName(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ECS_METADATA')) {
$this->roleName = Helper::env('ALIBABA_CLOUD_ECS_METADATA');
}
if (isset($params['roleName'])) {
$this->roleName = $params['roleName'];
}
}
private function filterDisableECSIMDSv1($params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_IMDSV1_DISABLED')) {
$this->disableIMDSv1 = Helper::env('ALIBABA_CLOUD_IMDSV1_DISABLED') === true ? true : false;
}
if (isset($params['disableIMDSv1'])) {
$this->disableIMDSv1 = $params['disableIMDSv1'];
}
}
/**
* Get credentials by request.
*
* @return RefreshResult
* @throws InvalidArgumentException
* @throws RuntimeException
* @throws GuzzleException
*/
public function refreshCredentials()
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ECS_METADATA_DISABLED') && Helper::env('ALIBABA_CLOUD_ECS_METADATA_DISABLED') === true) {
throw new RuntimeException('IMDS credentials is disabled');
}
if (is_null($this->roleName) || $this->roleName === '') {
$this->roleName = $this->getRoleNameFromMeta();
}
$url = $this->metadataHost . $this->ecsUri . $this->roleName;
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$metadataToken = $this->getMetadataToken();
if (!is_null($metadataToken)) {
$options['headers']['X-aliyun-ecs-metadata-token'] = $metadataToken;
}
$result = Request::createClient()->request('GET', $url, $options);
if ($result->getStatusCode() === 404) {
throw new InvalidArgumentException('The role was not found in the instance' . (string) $result);
}
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error refreshing credentials from IMDS, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result);
}
$credentials = $result->toArray();
if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) {
throw new RuntimeException('Error retrieving credentials from IMDS result:' . $result->toJson());
}
if (!isset($credentials['Code']) || $credentials['Code'] !== 'Success') {
throw new RuntimeException('Error retrieving credentials from IMDS result, Code is not Success:' . $result->toJson());
}
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => \strtotime($credentials['Expiration']),
'providerName' => $this->getProviderName(),
]), $this->getStaleTime(strtotime($credentials["Expiration"])), $this->getPrefetchTime(strtotime($credentials["Expiration"])));
}
/**
* @return string
* @throws InvalidArgumentException
* @throws RuntimeException
* @throws GuzzleException
*/
private function getRoleNameFromMeta()
{
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$metadataToken = $this->getMetadataToken();
if (!is_null($metadataToken)) {
$options['headers']['X-aliyun-ecs-metadata-token'] = $metadataToken;
}
$result = Request::createClient()->request(
'GET',
'http://100.100.100.200/latest/meta-data/ram/security-credentials/',
$options
);
if ($result->getStatusCode() === 404) {
throw new InvalidArgumentException('The role name was not found in the instance' . (string) $result);
}
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error retrieving role name from result: ' . (string) $result);
}
$role_name = (string) $result;
if (!$role_name) {
throw new RuntimeException('Error retrieving role name from result is empty');
}
return $role_name;
}
/**
* Get metadata token by request.
*
* @return string
* @throws RuntimeException
* @throws GuzzleException
*/
private function getMetadataToken()
{
$url = $this->metadataHost . $this->metadataTokenUri;
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$options['headers']['X-aliyun-ecs-metadata-token-ttl-seconds'] = $this->metadataTokenDuration;
$result = Request::createClient()->request('PUT', $url, $options);
if ($result->getStatusCode() != 200) {
if ($this->disableIMDSv1) {
throw new RuntimeException('Failed to get token from ECS Metadata Service. HttpCode= ' . $result->getStatusCode());
}
return null;
}
return (string) $result;
}
/**
* @var int
*/
public function getPrefetchTime($expiration)
{
return $expiration <= 0 ?
time() + (5 * 60) :
time() + (60 * 60);
}
/**
* @return string
*/
public function key()
{
return 'ecs_ram_role#roleName#' . $this->roleName;
}
/**
* @return string
*/
public function getProviderName()
{
return 'ecs_ram_role';
}
/**
* @return string
*/
public function getRoleName()
{
return $this->roleName;
}
/**
* @return bool
*/
public function isDisableIMDSv1()
{
return $this->disableIMDSv1;
}
}
@@ -0,0 +1,65 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use InvalidArgumentException;
/**
* @internal This class is intended for internal use within the package.
* Class EnvironmentVariableCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class EnvironmentVariableCredentialsProvider implements CredentialsProvider
{
/**
* EnvironmentVariableCredentialsProvider constructor.
*/
public function __construct() {}
/**
* Get credential.
*
* @return Credentials
* @throws InvalidArgumentException
*/
public function getCredentials()
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID')) {
$accessKeyId = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_ID');
} else {
throw new InvalidArgumentException('Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID)');
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET')) {
$accessKeySecret = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_SECRET');
} else {
throw new InvalidArgumentException('Access key Secret must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_SECRET)');
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_SECURITY_TOKEN')) {
$securityToken = Helper::env('ALIBABA_CLOUD_SECURITY_TOKEN');
return new Credentials([
'accessKeyId' => $accessKeyId,
'accessKeySecret' => $accessKeySecret,
'securityToken' => $securityToken,
'providerName' => $this->getProviderName(),
]);
}
return new Credentials([
'accessKeyId' => $accessKeyId,
'accessKeySecret' => $accessKeySecret,
'providerName' => $this->getProviderName(),
]);
}
/**
* @inheritDoc
*/
public function getProviderName()
{
return "env";
}
}
@@ -0,0 +1,207 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use InvalidArgumentException;
use RuntimeException;
/**
* @internal This class is intended for internal use within the package.
* Class ExternalCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class ExternalCredentialsProvider implements CredentialsProvider
{
/**
* @var string
*/
private $processCommand;
/**
* @var int
*/
private $timeout = 60;
/**
* @var callable|null
*/
private $credentialUpdateCallback;
/**
* @var Credentials|null
*/
private $credentials;
/**
* @var int
*/
private $expirationTimestamp = 0;
/**
* ExternalCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
if (isset($options['timeout'])) {
$this->timeout = (int) $options['timeout'];
}
$this->processCommand = isset($params['processCommand']) ? $params['processCommand'] : null;
$this->credentialUpdateCallback = isset($params['credentialUpdateCallback'])
? $params['credentialUpdateCallback'] : null;
if (empty($this->processCommand)) {
throw new InvalidArgumentException('process_command is empty');
}
}
/**
* @return Credentials
*/
public function getCredentials()
{
if ($this->needUpdateCredential()) {
$credential = $this->getCredentialsInternal();
$this->credentials = $credential;
$this->expirationTimestamp = $credential->getExpiration() > 0 ? $credential->getExpiration() : 0;
$this->invokeCredentialUpdateCallback($credential);
}
return new Credentials([
'accessKeyId' => $this->credentials->getAccessKeyId(),
'accessKeySecret' => $this->credentials->getAccessKeySecret(),
'securityToken' => $this->credentials->getSecurityToken(),
'expiration' => $this->credentials->getExpiration(),
'providerName' => $this->getProviderName(),
]);
}
/**
* @return Credentials
*/
public function getCredentialsInternal()
{
$stdout = $this->executeCommand();
$json = json_decode($stdout, true);
if (!is_array($json)) {
throw new RuntimeException('failed to parse external command output: ' . json_last_error_msg());
}
if (empty($json['access_key_id']) || empty($json['access_key_secret'])) {
throw new RuntimeException('invalid credential response: access_key_id or access_key_secret is empty');
}
if (isset($json['mode']) && $json['mode'] === 'StsToken' && empty($json['sts_token'])) {
throw new RuntimeException('invalid StsToken credential response: sts_token is empty');
}
$expiration = !empty($json['expiration']) ? strtotime($json['expiration']) : 0;
if ($expiration === false) {
$expiration = 0;
}
return new Credentials([
'accessKeyId' => $json['access_key_id'],
'accessKeySecret' => $json['access_key_secret'],
'securityToken' => isset($json['sts_token']) ? $json['sts_token'] : null,
'expiration' => $expiration,
'providerName' => $this->getProviderName(),
]);
}
/**
* @return string
*/
private function executeCommand()
{
$args = preg_split('/\s+/', trim($this->processCommand));
if (empty($args) || $args[0] === '') {
throw new RuntimeException('process_command is empty');
}
$command = implode(' ', array_map('escapeshellarg', $args));
$descriptorSpec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($command, $descriptorSpec, $pipes);
if (!is_resource($process)) {
throw new RuntimeException('failed to execute external command');
}
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);
$stdout = '';
$stderr = '';
$startedAt = time();
while (true) {
$stdout .= stream_get_contents($pipes[1]);
$stderr .= stream_get_contents($pipes[2]);
$status = proc_get_status($process);
if (!$status['running']) {
$exitCode = $status['exitcode'];
break;
}
if (time() - $startedAt >= $this->timeout) {
proc_terminate($process);
proc_close($process);
throw new RuntimeException('command process timed out after ' . ($this->timeout * 1000) . ' milliseconds');
}
usleep(10000);
}
$stdout .= stream_get_contents($pipes[1]);
$stderr .= stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if ($exitCode !== 0) {
throw new RuntimeException('failed to execute external command: exit status ' . $exitCode . "\nstderr: " . $stderr);
}
return $stdout;
}
/**
* @return bool
*/
private function needUpdateCredential()
{
if ($this->credentials === null) {
return true;
}
if ($this->expirationTimestamp === 0) {
return true;
}
return $this->expirationTimestamp - time() <= 180;
}
private function invokeCredentialUpdateCallback(Credentials $credential)
{
if (!$this->credentialUpdateCallback || !is_callable($this->credentialUpdateCallback)) {
return;
}
try {
call_user_func(
$this->credentialUpdateCallback,
$credential->getAccessKeyId(),
$credential->getAccessKeySecret(),
$credential->getSecurityToken(),
$this->expirationTimestamp
);
} catch (\Exception $e) {
// Warning only
}
}
/**
* @return string
*/
public function getProviderName()
{
return 'external';
}
}
@@ -0,0 +1,220 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Request\Request;
use AlibabaCloud\Credentials\Credential\RefreshResult;
use InvalidArgumentException;
use RuntimeException;
/**
* @internal This class is intended for internal use within the package.
* Class OAuthCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class OAuthCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $clientId;
/**
* @var string
*/
private $signInUrl;
/**
* @var string
*/
private $refreshToken;
/**
* @var string
*/
private $accessToken;
/**
* @var int
*/
private $accessTokenExpire;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 10;
/**
* @var callable|null
*/
private $tokenUpdateCallback;
/**
* OAuthCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterParams($params);
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
}
private function filterParams(array $params)
{
$this->clientId = isset($params['clientId']) ? $params['clientId'] : null;
$this->signInUrl = isset($params['signInUrl']) ? $params['signInUrl'] : null;
$this->refreshToken = isset($params['refreshToken']) ? $params['refreshToken'] : null;
$this->accessToken = isset($params['accessToken']) ? $params['accessToken'] : null;
$this->accessTokenExpire = isset($params['accessTokenExpire']) ? (int) $params['accessTokenExpire'] : 0;
$this->tokenUpdateCallback = isset($params['tokenUpdateCallback']) ? $params['tokenUpdateCallback'] : null;
if (empty($this->clientId)) {
throw new InvalidArgumentException('The clientId is empty.');
}
if (empty($this->signInUrl)) {
throw new InvalidArgumentException('The url for sign-in is empty.');
}
}
private function tryRefreshOAuthToken()
{
$parsedUrl = parse_url($this->signInUrl);
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] : 'https';
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
$requestUrl = $scheme . '://' . $host . '/v1/token';
$formData = [
'grant_type' => 'refresh_token',
'refresh_token' => $this->refreshToken,
'client_id' => $this->clientId,
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
];
$options = [
'http_errors' => false,
'connect_timeout' => $this->connectTimeout,
'read_timeout' => $this->readTimeout,
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
],
'form_params' => $formData,
];
$result = Request::createClient()->request('POST', $requestUrl, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Failed to refresh OAuth token, status code: ' . $result->getStatusCode());
}
$json = json_decode((string) $result->getBody(), true);
if (empty($json) || empty($json['access_token']) || empty($json['refresh_token'])) {
throw new RuntimeException('Failed to refresh OAuth token: ' . (string) $result->getBody());
}
$this->accessToken = $json['access_token'];
$this->refreshToken = $json['refresh_token'];
$this->accessTokenExpire = time() + (isset($json['expires_in']) ? (int) $json['expires_in'] : 3600);
}
/**
* @return RefreshResult
* @throws RuntimeException
*/
public function refreshCredentials()
{
$now = time();
if (!empty($this->refreshToken)
&& (empty($this->accessToken) || $this->accessTokenExpire === 0
|| $this->accessTokenExpire - $now <= 1200)) {
$this->tryRefreshOAuthToken();
}
$parsedUrl = parse_url($this->signInUrl);
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] : 'https';
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
$requestUrl = $scheme . '://' . $host . '/v1/exchange';
$options = [
'http_errors' => false,
'connect_timeout' => $this->connectTimeout,
'read_timeout' => $this->readTimeout,
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->accessToken,
],
];
$result = Request::createClient()->request('POST', $requestUrl, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Get session token from OAuth failed, statusCode: '
. $result->getStatusCode() . ', result: ' . (string) $result->getBody());
}
$json = json_decode((string) $result->getBody(), true);
if (empty($json) || empty($json['accessKeyId']) || empty($json['accessKeySecret'])
|| empty($json['securityToken'])) {
throw new RuntimeException('Refresh session token from OAuth failed, fail to get credentials: '
. (string) $result->getBody());
}
$expiration = isset($json['expiration']) ? \strtotime($json['expiration']) : 0;
if ($this->tokenUpdateCallback && is_callable($this->tokenUpdateCallback)) {
try {
call_user_func(
$this->tokenUpdateCallback,
$this->refreshToken,
$this->accessToken,
$json['accessKeyId'],
$json['accessKeySecret'],
$json['securityToken'],
$this->accessTokenExpire,
$expiration
);
} catch (\Exception $e) {
// Warning only
}
}
return new RefreshResult(new Credentials([
'accessKeyId' => $json['accessKeyId'],
'accessKeySecret' => $json['accessKeySecret'],
'securityToken' => $json['securityToken'],
'expiration' => $expiration,
'providerName' => $this->getProviderName(),
]), $this->getStaleTime($expiration));
}
public function key()
{
return 'oauth#' . $this->signInUrl . '#' . $this->clientId;
}
public function getProviderName()
{
return 'oauth';
}
}
@@ -0,0 +1,268 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Request\Request;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
use Exception;
use AlibabaCloud\Credentials\Credential\RefreshResult;
/**
* @internal This class is intended for internal use within the package.
* Class OIDCRoleArnCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class OIDCRoleArnCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $roleArn;
/**
* @var string
*/
private $oidcProviderArn;
/**
* @var string
*/
private $oidcTokenFilePath;
/**
* @var string
*/
private $roleSessionName;
/**
* @description role session expiration
* @example 3600
* @var int
*/
private $durationSeconds = 3600;
/**
* @var string
*/
private $policy;
/**
* @var string
*/
private $stsEndpoint;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 5;
/**
* OIDCRoleArnCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterRoleArn($params);
$this->filterOIDCProviderArn($params);
$this->filterOIDCTokenFilePath($params);
$this->filterRoleSessionName($params);
$this->filterDurationSeconds($params);
$this->filterPolicy($params);
$this->filterSTSEndpoint($params);
}
private function filterRoleArn(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN')) {
$this->roleArn = Helper::env('ALIBABA_CLOUD_ROLE_ARN');
}
if (isset($params['roleArn'])) {
$this->roleArn = $params['roleArn'];
}
Filter::roleArn($this->roleArn);
}
private function filterOIDCProviderArn(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_PROVIDER_ARN')) {
$this->oidcProviderArn = Helper::env('ALIBABA_CLOUD_OIDC_PROVIDER_ARN');
}
if (isset($params['oidcProviderArn'])) {
$this->oidcProviderArn = $params['oidcProviderArn'];
}
Filter::oidcProviderArn($this->oidcProviderArn);
}
private function filterOIDCTokenFilePath(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_TOKEN_FILE')) {
$this->oidcTokenFilePath = Helper::env('ALIBABA_CLOUD_OIDC_TOKEN_FILE');
}
if (isset($params['oidcTokenFilePath'])) {
$this->oidcTokenFilePath = $params['oidcTokenFilePath'];
}
Filter::oidcTokenFilePath($this->oidcTokenFilePath);
}
private function filterRoleSessionName(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_SESSION_NAME')) {
$this->roleSessionName = Helper::env('ALIBABA_CLOUD_ROLE_SESSION_NAME');
}
if (isset($params['roleSessionName'])) {
$this->roleSessionName = $params['roleSessionName'];
}
if (is_null($this->roleSessionName) || $this->roleSessionName === '') {
$this->roleSessionName = 'phpSdkRoleSessionName';
}
}
private function filterDurationSeconds(array $params)
{
if (isset($params['durationSeconds'])) {
if (is_int($params['durationSeconds'])) {
$this->durationSeconds = $params['durationSeconds'];
}
}
if ($this->durationSeconds < 900) {
throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration');
}
}
private function filterPolicy(array $params)
{
if (isset($params['policy'])) {
if (is_string($params['policy'])) {
$this->policy = $params['policy'];
}
if (is_array($params['policy'])) {
$this->policy = json_encode($params['policy']);
}
}
}
private function filterSTSEndpoint(array $params)
{
$prefix = 'sts';
if (Helper::envNotEmpty('ALIBABA_CLOUD_VPC_ENDPOINT_ENABLED') || (isset($params['enableVpc']) && $params['enableVpc'] === true)) {
$prefix = 'sts-vpc';
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_STS_REGION')) {
$this->stsEndpoint = $prefix . '.' . Helper::env('ALIBABA_CLOUD_STS_REGION') . '.aliyuncs.com';
}
if (isset($params['stsRegionId'])) {
$this->stsEndpoint = $prefix . '.' . $params['stsRegionId'] . '.aliyuncs.com';
}
if (isset($params['stsEndpoint'])) {
$this->stsEndpoint = $params['stsEndpoint'];
}
if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') {
$this->stsEndpoint = 'sts.aliyuncs.com';
}
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
Filter::timeout($this->connectTimeout, $this->readTimeout);
}
/**
* Get credentials by request.
*
* @return RefreshResult
* @throws RuntimeException
* @throws GuzzleException
*/
public function refreshCredentials()
{
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$options['query']['Action'] = 'AssumeRoleWithOIDC';
$options['query']['Version'] = '2015-04-01';
$options['query']['Format'] = 'JSON';
$options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
$options['query']['RoleArn'] = $this->roleArn;
$options['query']['OIDCProviderArn'] = $this->oidcProviderArn;
try {
$oidcToken = file_get_contents($this->oidcTokenFilePath);
$options['query']['OIDCToken'] = $oidcToken;
} catch (Exception $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
$options['query']['RoleSessionName'] = $this->roleSessionName;
$options['query']['DurationSeconds'] = (string) $this->durationSeconds;
if (!is_null($this->policy)) {
$options['query']['Policy'] = $this->policy;
}
$url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint);
$result = Request::createClient()->request('POST', $url, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error refreshing credentials from OIDC, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result);
}
$json = $result->toArray();
$credentials = $json['Credentials'];
if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) {
throw new RuntimeException('Error retrieving credentials from OIDC result:' . $result->toJson());
}
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => \strtotime($credentials['Expiration']),
'providerName' => $this->getProviderName(),
]), $this->getStaleTime(strtotime($credentials['Expiration'])));
}
public function key()
{
return 'oidc_role_arn#roleArn#' . $this->roleArn . '#oidcProviderArn#' . $this->oidcProviderArn . '#roleSessionName#' . $this->roleSessionName;
}
public function getProviderName()
{
return 'oidc_role_arn';
}
}
@@ -0,0 +1,188 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use RuntimeException;
/**
* @internal This class is intended for internal use within the package.
* Class ProfileCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class ProfileCredentialsProvider implements CredentialsProvider
{
/**
* @var string
*/
private $profileName;
/**
* @var string
*/
private $profileFile;
/**
* @var CredentialsProvider
*/
private $credentialsProvider;
/**
* ProfileCredentialsProvider constructor.
*
* @param array $params
*/
public function __construct(array $params = [])
{
$this->filterProfileName($params);
$this->filterProfileFile();
}
private function filterProfileName(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_PROFILE')) {
$this->profileName = Helper::env('ALIBABA_CLOUD_PROFILE');
}
if (isset($params['profileName'])) {
$this->profileName = $params['profileName'];
}
if (is_null($this->profileName) || $this->profileName === '') {
$this->profileName = 'default';
}
}
private function filterProfileFile()
{
$this->profileFile = Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_FILE');
if (!$this->profileFile) {
$this->profileFile = self::getDefaultFile();
}
}
/**
* @return bool
*/
private function shouldReloadCredentialsProvider()
{
if (is_null($this->credentialsProvider)) {
return true;
}
return false;
}
/**
* @return CredentialsProvider
*/
private function reloadCredentialsProvider($profileFile, $profileName)
{
if (!Helper::inOpenBasedir($profileFile)) {
throw new RuntimeException('Unable to open credentials file: ' . $profileFile);
}
if (!\is_readable($profileFile) || !\is_file($profileFile)) {
throw new RuntimeException('Credentials file is not readable: ' . $profileFile);
}
$fileArray = \parse_ini_file($profileFile, true);
if (\is_array($fileArray) && !empty($fileArray)) {
$credentialsConfigures = [];
foreach (\array_change_key_case($fileArray) as $name => $configures) {
if ($name === $profileName) {
$credentialsConfigures = $configures;
break;
}
}
if (\is_array($credentialsConfigures) && !empty($credentialsConfigures)) {
switch (Helper::unsetReturnNull($credentialsConfigures, 'type')) {
case 'access_key':
return new StaticAKCredentialsProvider([
'accessKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_id'),
'accessKeySecret' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_secret'),
]);
case 'ram_role_arn':
$innerProvider = new StaticAKCredentialsProvider([
'accessKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_id'),
'accessKeySecret' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_secret'),
]);
return new RamRoleArnCredentialsProvider([
'credentialsProvider' => $innerProvider,
'roleArn' => Helper::unsetReturnNull($credentialsConfigures, 'role_arn'),
'roleSessionName' => Helper::unsetReturnNull($credentialsConfigures, 'role_session_name'),
'policy' => Helper::unsetReturnNull($credentialsConfigures, 'policy'),
]);
case 'ecs_ram_role':
return new EcsRamRoleCredentialsProvider([
'roleName' => Helper::unsetReturnNull($credentialsConfigures, 'role_name'),
]);
case 'oidc_role_arn':
return new OIDCRoleArnCredentialsProvider([
'roleArn' => Helper::unsetReturnNull($credentialsConfigures, 'role_arn'),
'oidcProviderArn' => Helper::unsetReturnNull($credentialsConfigures, 'oidc_provider_arn'),
'oidcTokenFilePath' => Helper::unsetReturnNull($credentialsConfigures, 'oidc_token_file_path'),
'roleSessionName' => Helper::unsetReturnNull($credentialsConfigures, 'role_session_name'),
'policy' => Helper::unsetReturnNull($credentialsConfigures, 'policy'),
]);
case 'rsa_key_pair':
return new RsaKeyPairCredentialsProvider([
'publicKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'public_key_id'),
'privateKeyFile' => Helper::unsetReturnNull($credentialsConfigures, 'private_key_file'),
]);
default:
throw new RuntimeException('Unsupported credential type from credentials file: ' . Helper::unsetReturnNull($credentialsConfigures, 'type'));
}
}
}
throw new RuntimeException('Failed to get credential from credentials file: ' . $profileFile);
}
/**
* Get credential.
*
* @return Credentials
* @throws RuntimeException
*/
public function getCredentials()
{
if ($this->shouldReloadCredentialsProvider()) {
$this->credentialsProvider = $this->reloadCredentialsProvider($this->profileFile, $this->profileName);
}
$credentials = $this->credentialsProvider->getCredentials();
return new Credentials([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'providerName' => $this->getProviderName() . '/' . $this->credentialsProvider->getProviderName(),
]);
}
/**
* Get the default credential file.
*
* @return string
*/
private function getDefaultFile()
{
return Helper::getHomeDirectory() .
DIRECTORY_SEPARATOR .
'.alibabacloud' .
DIRECTORY_SEPARATOR .
'credentials';
}
/**
* @return string
*/
public function getProviderName()
{
return 'profile';
}
}
@@ -0,0 +1,321 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Request\Request;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
use AlibabaCloud\Credentials\Credential\RefreshResult;
/**
* @internal This class is intended for internal use within the package.
* Class RamRoleArnCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class RamRoleArnCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var CredentialsProvider
*/
private $credentialsProvider;
/**
* @var string
*/
private $roleArn;
/**
* @var string
*/
private $roleSessionName;
/**
* @description role session expiration
* @example 3600
* @var int
*/
private $durationSeconds = 3600;
/**
* @var string
*/
private $externalId;
/**
* @var string
*/
private $policy;
/**
* @var string
*/
private $stsEndpoint;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 5;
/**
* RamRoleArnCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterCredentials($params);
$this->filterRoleArn($params);
$this->filterRoleSessionName($params);
$this->filterDurationSeconds($params);
$this->filterPolicy($params);
$this->filterExternalId($params);
$this->filterSTSEndpoint($params);
}
private function filterRoleArn(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN')) {
$this->roleArn = Helper::env('ALIBABA_CLOUD_ROLE_ARN');
}
if (isset($params['roleArn'])) {
$this->roleArn = $params['roleArn'];
}
Filter::roleArn($this->roleArn);
}
private function filterRoleSessionName(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_SESSION_NAME')) {
$this->roleSessionName = Helper::env('ALIBABA_CLOUD_ROLE_SESSION_NAME');
}
if (isset($params['roleSessionName'])) {
$this->roleSessionName = $params['roleSessionName'];
}
if (is_null($this->roleSessionName) || $this->roleSessionName === '') {
$this->roleSessionName = 'phpSdkRoleSessionName';
}
}
private function filterDurationSeconds(array $params)
{
if (isset($params['durationSeconds'])) {
if (is_int($params['durationSeconds'])) {
$this->durationSeconds = $params['durationSeconds'];
}
}
if ($this->durationSeconds < 900) {
throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration');
}
}
private function filterPolicy(array $params)
{
if (isset($params['policy'])) {
if (is_string($params['policy'])) {
$this->policy = $params['policy'];
}
if (is_array($params['policy'])) {
$this->policy = json_encode($params['policy']);
}
}
}
private function filterExternalId(array $params)
{
if (isset($params['externalId'])) {
if (is_string($params['externalId'])) {
$this->externalId = $params['externalId'];
}
}
}
private function filterSTSEndpoint(array $params)
{
$prefix = 'sts';
if (Helper::envNotEmpty('ALIBABA_CLOUD_VPC_ENDPOINT_ENABLED') || (isset($params['enableVpc']) && $params['enableVpc'] === true)) {
$prefix = 'sts-vpc';
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_STS_REGION')) {
$this->stsEndpoint = $prefix . '.' . Helper::env('ALIBABA_CLOUD_STS_REGION') . '.aliyuncs.com';
}
if (isset($params['stsRegionId'])) {
$this->stsEndpoint = $prefix . '.' . $params['stsRegionId'] . '.aliyuncs.com';
}
if (isset($params['stsEndpoint'])) {
$this->stsEndpoint = $params['stsEndpoint'];
}
if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') {
$this->stsEndpoint = 'sts.aliyuncs.com';
}
}
private function filterCredentials(array $params)
{
if (isset($params['credentialsProvider'])) {
if (!($params['credentialsProvider'] instanceof CredentialsProvider)) {
throw new InvalidArgumentException('Invalid credentialsProvider option for ram_role_arn');
}
$this->credentialsProvider = $params['credentialsProvider'];
} else if (isset($params['accessKeyId']) && isset($params['accessKeySecret']) && isset($params['securityToken'])) {
Filter::accessKey($params['accessKeyId'], $params['accessKeySecret']);
Filter::securityToken($params['securityToken']);
$this->credentialsProvider = new StaticSTSCredentialsProvider($params);
} else if (isset($params['accessKeyId']) && isset($params['accessKeySecret'])) {
Filter::accessKey($params['accessKeyId'], $params['accessKeySecret']);
$this->credentialsProvider = new StaticAKCredentialsProvider($params);
} else {
throw new InvalidArgumentException('Missing required credentials option for ram_role_arn');
}
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
Filter::timeout($this->connectTimeout, $this->readTimeout);
}
/**
* Get credentials by request.
*
* @return RefreshResult
* @throws RuntimeException
* @throws GuzzleException
*/
public function refreshCredentials()
{
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$options['query']['Action'] = 'AssumeRole';
$options['query']['Version'] = '2015-04-01';
$options['query']['Format'] = 'JSON';
$options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
$options['query']['SignatureMethod'] = 'HMAC-SHA1';
$options['query']['SignatureVersion'] = '1.0';
$options['query']['SignatureNonce'] = Request::uuid(json_encode($options['query']));
$options['query']['RoleArn'] = $this->roleArn;
$options['query']['RoleSessionName'] = $this->roleSessionName;
$options['query']['DurationSeconds'] = (string) $this->durationSeconds;
if (!is_null($this->policy) && $this->policy !== '') {
$options['query']['Policy'] = $this->policy;
}
if (!is_null($this->externalId) && $this->externalId !== '') {
$options['query']['ExternalId'] = $this->externalId;
}
$sessionCredentials = $this->credentialsProvider->getCredentials();
$options['query']['AccessKeyId'] = $sessionCredentials->getAccessKeyId();
if (!is_null($sessionCredentials->getSecurityToken())) {
$options['query']['SecurityToken'] = $sessionCredentials->getSecurityToken();
}
$options['query']['Signature'] = Request::shaHmac1sign(
Request::signString('GET', $options['query']),
$sessionCredentials->getAccessKeySecret() . '&'
);
$url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint);
$result = Request::createClient()->request('GET', $url, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error refreshing credentials from RamRoleArn, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result);
}
$json = $result->toArray();
$credentials = $json['Credentials'];
if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) {
throw new RuntimeException('Error retrieving credentials from RamRoleArn result:' . $result->toJson());
}
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => \strtotime($credentials['Expiration']),
'providerName' => $this->getProviderName(),
]), $this->getStaleTime(strtotime($credentials['Expiration'])));
}
public function key()
{
$credentials = $this->credentialsProvider->getCredentials();
return 'ram_role_arn#credential#' . $credentials->getAccessKeyId() . '#roleArn#' . $this->roleArn . '#roleSessionName#' . $this->roleSessionName;
}
public function getProviderName()
{
return 'ram_role_arn/' . $this->credentialsProvider->getProviderName();
}
/**
* @return string
*/
public function getRoleArn()
{
return $this->roleArn;
}
/**
* @return string
*/
public function getRoleSessionName()
{
return $this->roleSessionName;
}
/**
* @return string
*/
public function getPolicy()
{
return $this->policy;
}
/**
* @deprecated
* @return string
*/
public function getOriginalAccessKeyId()
{
return $this->credentialsProvider->getCredentials()->getAccessKeyId();
}
/**
* @deprecated
* @return string
*/
public function getOriginalAccessKeySecret()
{
return $this->credentialsProvider->getCredentials()->getAccessKeySecret();
}
}
@@ -0,0 +1,200 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Request\Request;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Exception\GuzzleException;
use AlibabaCloud\Credentials\Credential\RefreshResult;
use InvalidArgumentException;
use RuntimeException;
use Exception;
/**
* @internal This class is intended for internal use within the package.
* Class RsaKeyPairCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class RsaKeyPairCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $publicKeyId;
/**
* @var string
*/
private $privateKey;
/**
* @description role session expiration
* @example 3600
* @var int
*/
private $durationSeconds = 3600;
/**
* @var string
*/
private $stsEndpoint;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 5;
/**
* RsaKeyPairCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterDurationSeconds($params);
$this->filterSTSEndpoint($params);
$this->publicKeyId = isset($params['publicKeyId']) ? $params['publicKeyId'] : null;
$privateKeyFile = isset($params['privateKeyFile']) ? $params['privateKeyFile'] : null;
Filter::publicKeyId($this->publicKeyId);
Filter::privateKeyFile($privateKeyFile);
try {
$this->privateKey = file_get_contents($privateKeyFile);
} catch (Exception $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
Filter::timeout($this->connectTimeout, $this->readTimeout);
}
private function filterDurationSeconds(array $params)
{
if (isset($params['durationSeconds'])) {
if (is_int($params['durationSeconds'])) {
$this->durationSeconds = $params['durationSeconds'];
}
}
if ($this->durationSeconds < 900) {
throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration');
}
}
private function filterSTSEndpoint(array $params)
{
if (isset($params['stsEndpoint'])) {
$this->stsEndpoint = $params['stsEndpoint'];
}
if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') {
$this->stsEndpoint = 'sts.ap-northeast-1.aliyuncs.com';
}
}
/**
* Get credentials by request.
*
* @return RefreshResult
* @throws RuntimeException
* @throws GuzzleException
*/
public function refreshCredentials()
{
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$options['query']['Action'] = 'GenerateSessionAccessKey';
$options['query']['Version'] = '2015-04-01';
$options['query']['Format'] = 'JSON';
$options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
$options['query']['SignatureMethod'] = 'SHA256withRSA';
$options['query']['SignatureType'] = 'PRIVATEKEY';
$options['query']['SignatureVersion'] = '1.0';
$options['query']['SignatureNonce'] = Request::uuid(json_encode($options['query']));
$options['query']['DurationSeconds'] = (string) $this->durationSeconds;
$options['query']['AccessKeyId'] = $this->publicKeyId;
$options['query']['Signature'] = Request::shaHmac256WithRsasign(
Request::signString('GET', $options['query']),
$this->privateKey
);
$url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint);
$result = Request::createClient()->request('GET', $url, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error refreshing credentials from RsaKeyPair, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result);
}
$json = $result->toArray();
if (!isset($json['SessionAccessKey']['SessionAccessKeyId']) || !isset($json['SessionAccessKey']['SessionAccessKeySecret'])) {
throw new RuntimeException('Error retrieving credentials from RsaKeyPair result:' . $result->toJson());
}
$credentials = [];
$credentials['AccessKeyId'] = $json['SessionAccessKey']['SessionAccessKeyId'];
$credentials['AccessKeySecret'] = $json['SessionAccessKey']['SessionAccessKeySecret'];
$credentials['Expiration'] = $json['SessionAccessKey']['Expiration'];
$credentials['SecurityToken'] = null;
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => \strtotime($credentials['Expiration']),
'providerName' => $this->getProviderName(),
]), $this->getStaleTime(strtotime($credentials['Expiration'])));
}
public function key()
{
return 'rsa_key_pair#publicKeyId#' . $this->publicKeyId;
}
public function getProviderName()
{
return 'rsa_key_pair';
}
/**
* @return string
*/
public function getPublicKeyId()
{
return $this->publicKeyId;
}
/**
* @return mixed
*/
public function getPrivateKey()
{
return $this->privateKey;
}
}
@@ -0,0 +1,161 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Credential\RefreshResult;
abstract class SessionCredentialsProvider implements CredentialsProvider
{
/**
* @var array
*/
protected static $credentialsCache = [];
/**
* Expiration time slot for temporary security credentials.
*
* @var int
*/
protected $expirationSlot = 180;
/**
* @var string
*/
protected $error = 'Result contains no credentials';
/**
* Get the credentials from the cache in the validity period.
*
* @return RefreshResult|null
*/
protected function getCredentialsInCache()
{
if (isset(self::$credentialsCache[$this->key()])) {
$result = self::$credentialsCache[$this->key()];
return $result;
}
return null;
}
/**
* Cache credentials.
*
* @param RefreshResult $credential
*/
protected function cache(RefreshResult $credential)
{
self::$credentialsCache[$this->key()] = $credential;
}
/**
* Get credential.
*
* @return Credentials
*/
public function getCredentials()
{
if ($this->cacheIsStale() || $this->shouldInitiateCachePrefetch()) {
$result = $this->refreshCache();
$this->cache($result);
}
$result = $this->getCredentialsInCache();
return $result->credentials();
}
/**
* @return RefreshResult
*/
protected function refreshCache()
{
try {
return $this->handleFetchedSuccess($this->refreshCredentials());
} catch (\Exception $e) {
return $this->handleFetchedFailure($e);
}
}
/**
* @return RefreshResult
* @throws \Exception
*/
protected function handleFetchedFailure(\Exception $e)
{
$currentCachedValue = $this->getCredentialsInCache();
if (is_null($currentCachedValue)) {
throw $e;
}
if (time() < $currentCachedValue->staleTime()) {
return $currentCachedValue;
}
throw $e;
}
/**
* @return RefreshResult
*/
protected function handleFetchedSuccess(RefreshResult $value)
{
$now = time();
// 过期时间大于15分钟,不用管
if ($now < $value->staleTime()) {
return $value;
}
// 不足或等于15分钟,但未过期,下次会再次刷新
if ($now < $value->staleTime() + 15 * 60) {
$value->staleTime = $now;
return $value;
}
// 已过期,看缓存,缓存若大于15分钟,返回缓存,若小于15分钟,则稍后重试
if (is_null($this->getCredentialsInCache())) {
throw new \Exception("The fetched credentials have expired and no cache is available.");
} else if ($now < $this->getCredentialsInCache()->staleTime()) {
return $this->getCredentialsInCache();
} else {
// 返回成功,延长有效期 1 分钟
$expectation = mt_rand(50, 70);
$value->staleTime = time() + $expectation;
return $value;
}
}
/**
* @return bool
*/
protected function cacheIsStale()
{
return is_null($this->getCredentialsInCache()) || time() >= $this->getCredentialsInCache()->staleTime();
}
/**
* @return bool
*/
protected function shouldInitiateCachePrefetch()
{
return is_null($this->getCredentialsInCache()) || time() >= $this->getCredentialsInCache()->prefetchTime();
}
/**
* @return int
*/
public function getStaleTime($expiration)
{
return $expiration <= 0 ?
time() + (60 * 60) :
$expiration - (15 * 60);
}
/**
* @return RefreshResult
*/
abstract function refreshCredentials();
/**
* Get the toString of the credentials provider as the key.
*
* @return string
*/
abstract function key();
}
@@ -0,0 +1,78 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
/**
* @internal This class is intended for internal use within the package.
* Class StaticAKCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class StaticAKCredentialsProvider implements CredentialsProvider
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* StaticAKCredentialsProvider constructor.
*
* @param array $params
*/
public function __construct(array $params = [])
{
$this->filterAK($params);
}
private function filterAK(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID')) {
$this->accessKeyId = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_ID');
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET')) {
$this->accessKeySecret = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_SECRET');
}
if (isset($params['accessKeyId'])) {
$this->accessKeyId = $params['accessKeyId'];
}
if (isset($params['accessKeySecret'])) {
$this->accessKeySecret = $params['accessKeySecret'];
}
Filter::accessKey($this->accessKeyId, $this->accessKeySecret);
}
/**
* Get credential.
*
* @return Credentials
*/
public function getCredentials()
{
return new Credentials([
'accessKeyId' => $this->accessKeyId,
'accessKeySecret' => $this->accessKeySecret,
'providerName' => $this->getProviderName(),
]);
}
/**
* @inheritDoc
*/
public function getProviderName()
{
return "static_ak";
}
}
@@ -0,0 +1,92 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
/**
* @internal This class is intended for internal use within the package.
* Class StaticSTSCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class StaticSTSCredentialsProvider implements CredentialsProvider
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* @var string
*/
private $securityToken;
/**
* StaticSTSCredentialsProvider constructor.
*
* @param array $params
*/
public function __construct(array $params = [])
{
$this->filterSTS($params);
}
private function filterSTS(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID')) {
$this->accessKeyId = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_ID');
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET')) {
$this->accessKeySecret = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_SECRET');
}
if (Helper::envNotEmpty('ALIBABA_CLOUD_SECURITY_TOKEN')) {
$this->securityToken = Helper::env('ALIBABA_CLOUD_SECURITY_TOKEN');
}
if (isset($params['accessKeyId'])) {
$this->accessKeyId = $params['accessKeyId'];
}
if (isset($params['accessKeySecret'])) {
$this->accessKeySecret = $params['accessKeySecret'];
}
if (isset($params['securityToken'])) {
$this->securityToken = $params['securityToken'];
}
Filter::accessKey($this->accessKeyId, $this->accessKeySecret);
Filter::securityToken($this->securityToken);
}
/**
* Get credential.
*
* @return Credentials
*/
public function getCredentials()
{
return new Credentials([
'accessKeyId' => $this->accessKeyId,
'accessKeySecret' => $this->accessKeySecret,
'securityToken' => $this->securityToken,
'providerName' => $this->getProviderName(),
]);
}
/**
* @inheritDoc
*/
public function getProviderName()
{
return "static_sts";
}
}
@@ -0,0 +1,126 @@
<?php
namespace AlibabaCloud\Credentials\Providers;
use AlibabaCloud\Credentials\Utils\Helper;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Request\Request;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
use RuntimeException;
use AlibabaCloud\Credentials\Credential\RefreshResult;
/**
* @internal This class is intended for internal use within the package.
* Class URLCredentialsProvider
*
* @package AlibabaCloud\Credentials\Providers
*/
class URLCredentialsProvider extends SessionCredentialsProvider
{
/**
* @var string
*/
private $credentialsURI;
/**
* @var int
*/
private $connectTimeout = 5;
/**
* @var int
*/
private $readTimeout = 5;
/**
* URLCredentialsProvider constructor.
*
* @param array $params
* @param array $options
*/
public function __construct(array $params = [], array $options = [])
{
$this->filterOptions($options);
$this->filterCredentialsURI($params);
}
private function filterOptions(array $options)
{
if (isset($options['connectTimeout'])) {
$this->connectTimeout = $options['connectTimeout'];
}
if (isset($options['readTimeout'])) {
$this->readTimeout = $options['readTimeout'];
}
Filter::timeout($this->connectTimeout, $this->readTimeout);
}
private function filterCredentialsURI(array $params)
{
if (Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_URI')) {
$this->credentialsURI = Helper::env('ALIBABA_CLOUD_CREDENTIALS_URI');
}
if (isset($params['credentialsURI'])) {
$this->credentialsURI = $params['credentialsURI'];
}
Filter::credentialsURI($this->credentialsURI);
}
/**
* Get credentials by request.
*
* @return RefreshResult
* @throws InvalidArgumentException
* @throws RuntimeException
* @throws GuzzleException
*/
public function refreshCredentials()
{
$options = Request::commonOptions();
$options['read_timeout'] = $this->readTimeout;
$options['connect_timeout'] = $this->connectTimeout;
$result = Request::createClient()->request('GET', $this->credentialsURI, $options);
if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error refreshing credentials from credentialsURI, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result);
}
$credentials = $result->toArray();
if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken']) || !isset($credentials['Expiration'])) {
throw new RuntimeException('Error retrieving credentials from credentialsURI result:' . $result->toJson());
}
return new RefreshResult(new Credentials([
'accessKeyId' => $credentials['AccessKeyId'],
'accessKeySecret' => $credentials['AccessKeySecret'],
'securityToken' => $credentials['SecurityToken'],
'expiration' => \strtotime($credentials['Expiration']),
'providerName' => $this->getProviderName(),
]), $this->getStaleTime(strtotime($credentials['Expiration'])));
}
/**
* @return string
*/
public function key()
{
return 'credential_uri#' . $this->credentialsURI;
}
/**
* @return string
*/
public function getProviderName()
{
return 'credential_uri';
}
}
@@ -0,0 +1,242 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Providers\RamRoleArnCredentialsProvider;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\ShaHmac1Signature;
use AlibabaCloud\Credentials\Utils\Filter;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
/**
* @deprecated
* Use the AssumeRole of the RAM account to complete the authentication.
*/
class RamRoleArnCredential implements CredentialsInterface
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* @var string
*/
private $roleArn;
/**
* @var string
*/
private $roleSessionName;
/**
* @var string
*/
private $policy;
/**
* @var array
*/
private $config;
/**
* RamRoleArnCredential constructor.
*
* @param array $credential
* @param array $config
*/
public function __construct(array $credential = [], array $config = [])
{
$this->filterParameters($credential);
$this->filterPolicy($credential);
Filter::accessKey($credential['access_key_id'], $credential['access_key_secret']);
$this->config = $config;
$this->accessKeyId = $credential['access_key_id'];
$this->accessKeySecret = $credential['access_key_secret'];
$this->roleArn = $credential['role_arn'];
$this->roleSessionName = $credential['role_session_name'];
}
/**
* @param array $credential
*/
private function filterParameters(array $credential)
{
if (!isset($credential['access_key_id'])) {
throw new InvalidArgumentException('Missing required access_key_id option in config for ram_role_arn');
}
if (!isset($credential['access_key_secret'])) {
throw new InvalidArgumentException('Missing required access_key_secret option in config for ram_role_arn');
}
if (!isset($credential['role_arn'])) {
throw new InvalidArgumentException('Missing required role_arn option in config for ram_role_arn');
}
if (!isset($credential['role_session_name'])) {
throw new InvalidArgumentException('Missing required role_session_name option in config for ram_role_arn');
}
}
/**
* @param array $credential
*/
private function filterPolicy(array $credential)
{
if (isset($credential['policy'])) {
if (is_string($credential['policy'])) {
$this->policy = $credential['policy'];
}
if (is_array($credential['policy'])) {
$this->policy = json_encode($credential['policy']);
}
}
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* @return string
*/
public function getRoleArn()
{
return $this->roleArn;
}
/**
* @return string
*/
public function getRoleSessionName()
{
return $this->roleSessionName;
}
/**
* @return string
*/
public function getPolicy()
{
return $this->policy;
}
/**
* @return string
*/
public function __toString()
{
return "$this->accessKeyId#$this->accessKeySecret#$this->roleArn#$this->roleSessionName";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return new ShaHmac1Signature();
}
/**
* @return string
*/
public function getOriginalAccessKeyId()
{
return $this->accessKeyId;
}
/**
* @return string
*/
public function getOriginalAccessKeySecret()
{
return $this->accessKeySecret;
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeyId()
{
return $this->getSessionCredential()->getAccessKeyId();
}
/**
* @return AlibabaCloud\Credentials\Providers\Credentials
* @throws Exception
* @throws GuzzleException
*/
protected function getSessionCredential()
{
$params = [
'accessKeyId' => $this->accessKeyId,
'accessKeySecret' => $this->accessKeyId,
'roleArn' => $this->roleArn,
'roleSessionName' => $this->roleSessionName,
'policy' => $this->policy,
];
return (new RamRoleArnCredentialsProvider($params))->getCredentials();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeySecret()
{
return $this->getSessionCredential()->getAccessKeySecret();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getSecurityToken()
{
return $this->getSessionCredential()->getSecurityToken();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getExpiration()
{
return $this->getSessionCredential()->getExpiration();
}
/**
* @inheritDoc
*/
public function getCredential()
{
$credentials = $this->getSessionCredential();
return new CredentialModel([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'type' => 'ram_role_arn',
]);
}
}
+167
View File
@@ -0,0 +1,167 @@
<?php
namespace AlibabaCloud\Credentials\Request;
use AlibabaCloud\Credentials\Credentials;
use AlibabaCloud\Credentials\Utils\Helper;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use AlibabaCloud\Tea\Response;
use Psr\Http\Message\ResponseInterface;
use Exception;
use InvalidArgumentException;
/**
* RESTful RPC Request.
*/
class Request
{
/**
* Request Connect Timeout
*/
const CONNECT_TIMEOUT = 5;
/**
* Request Read Timeout
*/
const READ_TIMEOUT = 5;
/**
* @var array
*/
private static $config = [];
/**
*
* @return array
*/
public static function commonOptions()
{
$options = [];
$options['http_errors'] = false;
$options['connect_timeout'] = self::CONNECT_TIMEOUT;
$options['read_timeout'] = self::READ_TIMEOUT;
$options['headers']['User-Agent'] = Helper::getUserAgent();
// Turn on debug mode based on environment variable.
if (strtolower(Helper::env('DEBUG')) === 'sdk') {
$options['debug'] = true;
}
return $options;
}
/**
* @param string $salt
*
* @return string
*/
public static function uuid($salt)
{
return md5($salt . uniqid(md5(microtime(true)), true));
}
/**
* @param string $method
* @param array $parameters
*
* @return string
*/
public static function signString($method, array $parameters)
{
ksort($parameters);
$canonicalized = '';
foreach ($parameters as $key => $value) {
$canonicalized .= '&' . self::percentEncode($key) . '=' . self::percentEncode($value);
}
return $method . '&%2F&' . self::percentEncode(substr($canonicalized, 1));
}
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public static function shaHmac1sign($string, $accessKeySecret)
{
return base64_encode(hash_hmac('sha1', $string, $accessKeySecret, true));
}
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public static function shaHmac256sign($string, $accessKeySecret)
{
return base64_encode(hash_hmac('sha256', $string, $accessKeySecret, true));
}
/**
* @param string $string
* @param string $privateKey
*
* @return string
*/
public static function shaHmac256WithRsasign($string, $privateKey)
{
$binarySignature = '';
try {
openssl_sign(
$string,
$binarySignature,
$privateKey,
\OPENSSL_ALGO_SHA256
);
} catch (Exception $exception) {
throw new InvalidArgumentException(
$exception->getMessage()
);
}
return base64_encode($binarySignature);
}
/**
* @param string $string
*
* @return null|string|string[]
*/
private static function percentEncode($string)
{
$result = rawurlencode($string);
$result = str_replace(['+', '*'], ['%20', '%2A'], $result);
$result = preg_replace('/%7E/', '~', $result);
return $result;
}
/**
* @return Client
* @throws Exception
*/
public static function createClient()
{
if (Credentials::hasMock()) {
$stack = HandlerStack::create(Credentials::getMock());
$history = Credentials::getHandlerHistory();
$stack->push($history);
} else {
$stack = HandlerStack::create();
}
$stack->push(Middleware::mapResponse(static function (ResponseInterface $response) {
return new Response($response);
}));
self::$config['handler'] = $stack;
return new Client(self::$config);
}
}
@@ -0,0 +1,185 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Providers\RsaKeyPairCredentialsProvider;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\ShaHmac1Signature;
use AlibabaCloud\Credentials\Utils\Filter;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use InvalidArgumentException;
/**
* @deprecated
* Use the RSA key pair to complete the authentication (supported only on Japanese site)
*/
class RsaKeyPairCredential implements CredentialsInterface
{
/**
* @var string
*/
private $publicKeyId;
/**
* @var string
*/
private $privateKeyFile;
/**
* @var string
*/
private $privateKey;
/**
* @var array
*/
private $config;
/**
* RsaKeyPairCredential constructor.
*
* @param string $public_key_id
* @param string $private_key_file
* @param array $config
*/
public function __construct($public_key_id, $private_key_file, array $config = [])
{
Filter::publicKeyId($public_key_id);
Filter::privateKeyFile($private_key_file);
$this->publicKeyId = $public_key_id;
$this->privateKeyFile = $private_key_file;
$this->config = $config;
try {
$this->privateKey = file_get_contents($private_key_file);
} catch (Exception $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* @return string
*/
public function getOriginalAccessKeyId()
{
return $this->getPublicKeyId();
}
/**
* @return string
*/
public function getPublicKeyId()
{
return $this->publicKeyId;
}
/**
* @return string
*/
public function getOriginalAccessKeySecret()
{
return $this->getPrivateKey();
}
/**
* @return mixed
*/
public function getPrivateKey()
{
return $this->privateKey;
}
/**
* @return string
*/
public function __toString()
{
return "publicKeyId#$this->publicKeyId";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return new ShaHmac1Signature();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeyId()
{
return $this->getSessionCredential()->getAccessKeyId();
}
/**
* @return AlibabaCloud\Credentials\Providers\Credentials
* @throws Exception
* @throws GuzzleException
*/
protected function getSessionCredential()
{
$params = [
'publicKeyId' => $this->publicKeyId,
'privateKeyFile' => $this->privateKeyFile,
];
return (new RsaKeyPairCredentialsProvider($params))->getCredentials();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getAccessKeySecret()
{
return $this->getSessionCredential()->getAccessKeySecret();
}
/**
* @return string
* @throws Exception
* @throws GuzzleException
*/
public function getSecurityToken()
{
return $this->getSessionCredential()->getSecurityToken();
}
/**
* @return int
* @throws Exception
* @throws GuzzleException
*/
public function getExpiration()
{
return $this->getSessionCredential()->getExpiration();
}
/**
* @inheritDoc
*/
public function getCredential()
{
$credentials = $this->getSessionCredential();
return new CredentialModel([
'accessKeyId' => $credentials->getAccessKeyId(),
'accessKeySecret' => $credentials->getAccessKeySecret(),
'securityToken' => $credentials->getSecurityToken(),
'type' => 'rsa_key_pair',
]);
}
}
@@ -0,0 +1,47 @@
<?php
namespace AlibabaCloud\Credentials\Signature;
/**
* Class BearerTokenSignature
*
* @package AlibabaCloud\Credentials\Signature
*/
class BearerTokenSignature implements SignatureInterface
{
/**
* @return string
*/
public function getMethod()
{
return '';
}
/**
* @return string
*/
public function getType()
{
return 'BEARERTOKEN';
}
/**
* @return string
*/
public function getVersion()
{
return '1.0';
}
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public function sign($string, $accessKeySecret)
{
return '';
}
}
@@ -0,0 +1,47 @@
<?php
namespace AlibabaCloud\Credentials\Signature;
/**
* Class ShaHmac1Signature
*
* @package AlibabaCloud\Credentials\Signature
*/
class ShaHmac1Signature implements SignatureInterface
{
/**
* @return string
*/
public function getMethod()
{
return 'HMAC-SHA1';
}
/**
* @return string
*/
public function getType()
{
return '';
}
/**
* @return string
*/
public function getVersion()
{
return '1.0';
}
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public function sign($string, $accessKeySecret)
{
return base64_encode(hash_hmac('sha1', $string, $accessKeySecret, true));
}
}
@@ -0,0 +1,47 @@
<?php
namespace AlibabaCloud\Credentials\Signature;
/**
* Class ShaHmac256Signature
*
* @package AlibabaCloud\Credentials\Signature
*/
class ShaHmac256Signature implements SignatureInterface
{
/**
* @return string
*/
public function getMethod()
{
return 'HMAC-SHA256';
}
/**
* @return string
*/
public function getType()
{
return '';
}
/**
* @return string
*/
public function getVersion()
{
return '1.0';
}
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public function sign($string, $accessKeySecret)
{
return base64_encode(hash_hmac('sha256', $string, $accessKeySecret, true));
}
}
@@ -0,0 +1,64 @@
<?php
namespace AlibabaCloud\Credentials\Signature;
use Exception;
use InvalidArgumentException;
/**
* Class ShaHmac256WithRsaSignature
*
* @package AlibabaCloud\Credentials\Signature
*/
class ShaHmac256WithRsaSignature implements SignatureInterface
{
/**
* @return string
*/
public function getMethod()
{
return 'SHA256withRSA';
}
/**
* @return string
*/
public function getType()
{
return 'PRIVATEKEY';
}
/**
* @return string
*/
public function getVersion()
{
return '1.0';
}
/**
* @param string $string
* @param string $privateKey
*
* @return string
*/
public function sign($string, $privateKey)
{
$binarySignature = '';
try {
openssl_sign(
$string,
$binarySignature,
$privateKey,
\OPENSSL_ALGO_SHA256
);
} catch (Exception $exception) {
throw new InvalidArgumentException(
$exception->getMessage()
);
}
return base64_encode($binarySignature);
}
}
@@ -0,0 +1,34 @@
<?php
namespace AlibabaCloud\Credentials\Signature;
/**
* Interface SignatureInterface
*
* @package AlibabaCloud\Credentials\Signature
*/
interface SignatureInterface
{
/**
* @return string
*/
public function getMethod();
/**
* @return string
*/
public function getVersion();
/**
* @param string $string
* @param string $accessKeySecret
*
* @return string
*/
public function sign($string, $accessKeySecret);
/**
* @return string
*/
public function getType();
}
+115
View File
@@ -0,0 +1,115 @@
<?php
namespace AlibabaCloud\Credentials;
use AlibabaCloud\Credentials\Utils\Filter;
use AlibabaCloud\Credentials\Credential\CredentialModel;
use AlibabaCloud\Credentials\Signature\ShaHmac1Signature;
/**
* @deprecated
* Use the STS Token to complete the authentication.
*/
class StsCredential implements CredentialsInterface
{
/**
* @var string
*/
private $accessKeyId;
/**
* @var string
*/
private $accessKeySecret;
/**
* @var string
*/
private $securityToken;
/**
* @var int
*/
private $expiration;
/**
* StsCredential constructor.
*
* @param string $access_key_id Access key ID
* @param string $access_key_secret Access Key Secret
* @param int $expiration
* @param string $security_token Security Token
*/
public function __construct($access_key_id, $access_key_secret, $expiration, $security_token = '')
{
Filter::accessKey($access_key_id, $access_key_secret);
Filter::expiration($expiration);
$this->accessKeyId = $access_key_id;
$this->accessKeySecret = $access_key_secret;
$this->expiration = $expiration;
$this->securityToken = $security_token;
}
/**
* @return int
*/
public function getExpiration()
{
return $this->expiration;
}
/**
* @return string
*/
public function getAccessKeyId()
{
return $this->accessKeyId;
}
/**
* @return string
*/
public function getAccessKeySecret()
{
return $this->accessKeySecret;
}
/**
* @return string
*/
public function getSecurityToken()
{
return $this->securityToken;
}
/**
* @return string
*/
public function __toString()
{
return "$this->accessKeyId#$this->accessKeySecret#$this->securityToken";
}
/**
* @return ShaHmac1Signature
*/
public function getSignature()
{
return new ShaHmac1Signature();
}
/**
* @inheritDoc
*/
public function getCredential()
{
return new CredentialModel([
'accessKeyId' => $this->accessKeyId,
'accessKeySecret' => $this->accessKeySecret,
'securityToken' => $this->securityToken,
'type' => 'sts',
]);
}
}
+233
View File
@@ -0,0 +1,233 @@
<?php
namespace AlibabaCloud\Credentials\Utils;
use InvalidArgumentException;
/**
* Class Filter
*
* @package AlibabaCloud\Credentials\Utils
*/
class Filter
{
/**
* @param $name
*
* @codeCoverageIgnore
* @return string
*/
public static function credentialName($name)
{
if (!is_string($name)) {
throw new InvalidArgumentException('Name must be a string');
}
if ($name === '') {
throw new InvalidArgumentException('Name cannot be empty');
}
return $name;
}
/**
* @param $bearerToken
*
* @return mixed
* @throws InvalidArgumentException
*/
public static function bearerToken($bearerToken)
{
if (!is_string($bearerToken)) {
throw new InvalidArgumentException('bearerToken must be a string');
}
if ($bearerToken === '') {
throw new InvalidArgumentException('bearerToken cannot be empty');
}
return $bearerToken;
}
/**
* @param $publicKeyId
*
* @return mixed
*/
public static function publicKeyId($publicKeyId)
{
if (!is_string($publicKeyId)) {
throw new InvalidArgumentException('publicKeyId must be a string');
}
if ($publicKeyId === '') {
throw new InvalidArgumentException('publicKeyId cannot be empty');
}
return $publicKeyId;
}
/**
* @param $privateKeyFile
*
* @return mixed
*/
public static function privateKeyFile($privateKeyFile)
{
if (!is_string($privateKeyFile)) {
throw new InvalidArgumentException('privateKeyFile must be a string');
}
if ($privateKeyFile === '') {
throw new InvalidArgumentException('privateKeyFile cannot be empty');
}
return $privateKeyFile;
}
/**
* @param string|null $roleName
*/
public static function roleName($roleName)
{
if ($roleName === null) {
return;
}
if (!is_string($roleName)) {
throw new InvalidArgumentException('roleName must be a string');
}
if ($roleName === '') {
throw new InvalidArgumentException('roleName cannot be empty');
}
}
/**
* @param boolean|null $disableIMDSv1
*/
public static function disableIMDSv1($disableIMDSv1)
{
if (!is_bool($disableIMDSv1)) {
throw new InvalidArgumentException('disableIMDSv1 must be a boolean');
}
}
/**
* @param string|null $roleArn
*/
public static function roleArn($roleArn)
{
if (is_null($roleArn) || $roleArn === '') {
throw new InvalidArgumentException('roleArn cannot be empty');
}
}
/**
* @param string|null $roleArn
*/
public static function oidcProviderArn($oidcProviderArn)
{
if (is_null($oidcProviderArn) || $oidcProviderArn === '') {
throw new InvalidArgumentException('oidcProviderArn cannot be empty');
}
}
/**
* @param string|null $roleArn
*/
public static function oidcTokenFilePath($oidcTokenFilePath)
{
if (is_null($oidcTokenFilePath) || $oidcTokenFilePath === '') {
throw new InvalidArgumentException('oidcTokenFilePath cannot be empty');
}
}
/**
* @param string $accessKeyId
* @param string $accessKeySecret
*/
public static function accessKey($accessKeyId, $accessKeySecret)
{
if (!is_string($accessKeyId)) {
throw new InvalidArgumentException('accessKeyId must be a string');
}
if ($accessKeyId === '') {
throw new InvalidArgumentException('accessKeyId cannot be empty');
}
if (!is_string($accessKeySecret)) {
throw new InvalidArgumentException('accessKeySecret must be a string');
}
if ($accessKeySecret === '') {
throw new InvalidArgumentException('accessKeySecret cannot be empty');
}
}
/**
* @param string $securityToken
*/
public static function securityToken($securityToken)
{
if (!is_string($securityToken)) {
throw new InvalidArgumentException('securityToken must be a string');
}
if ($securityToken === '') {
throw new InvalidArgumentException('securityToken cannot be empty');
}
}
/**
* @param int $expiration
*/
public static function expiration($expiration)
{
if (!is_int($expiration)) {
throw new InvalidArgumentException('expiration must be a int');
}
}
/**
* @param int $connectTimeout
* @param int $readTimeout
*/
public static function timeout($connectTimeout, $readTimeout)
{
if (!is_int($connectTimeout)) {
throw new InvalidArgumentException('connectTimeout must be a int');
}
if (!is_int($readTimeout)) {
throw new InvalidArgumentException('readTimeout must be a int');
}
}
/**
* @param string|null $credentialsURI
*/
public static function credentialsURI($credentialsURI)
{
if (!is_string($credentialsURI)) {
throw new InvalidArgumentException('credentialsURI must be a string');
}
if ($credentialsURI === '') {
throw new InvalidArgumentException('credentialsURI cannot be empty');
}
}
/**
* @param boolean|null $reuseLastProviderEnabled
*/
public static function reuseLastProviderEnabled($reuseLastProviderEnabled)
{
if (!is_bool($reuseLastProviderEnabled)) {
throw new InvalidArgumentException('reuseLastProviderEnabled must be a boolean');
}
}
}
+251
View File
@@ -0,0 +1,251 @@
<?php
namespace AlibabaCloud\Credentials\Utils;
use AlibabaCloud\Credentials\Credential;
use org\bovigo\vfs\vfsStream;
use Closure;
/**
* Class Helper
*
* @package AlibabaCloud\Credentials\Utils
*/
class Helper
{
/**
* @param array $arrays
*
* @return array
*/
public static function merge(array $arrays)
{
$result = [];
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (is_int($key)) {
$result[] = $value;
continue;
}
if (isset($result[$key]) && is_array($result[$key])) {
$result[$key] = self::merge(
[$result[$key], $value]
);
continue;
}
$result[$key] = $value;
}
}
return $result;
}
/**
* @param $filename
*
* @return bool
*/
public static function inOpenBasedir($filename)
{
$open_basedir = ini_get('open_basedir');
if (!$open_basedir) {
return true;
}
if (0 === strpos($filename, vfsStream::SCHEME)) {
// 虚拟文件忽略
return true;
}
$dirs = explode(PATH_SEPARATOR, $open_basedir);
return empty($dirs) || self::inDir($filename, $dirs);
}
/**
* @param string $filename
* @param array $dirs
*
* @return bool
*/
public static function inDir($filename, array $dirs)
{
foreach ($dirs as $dir) {
if ($dir[strlen($dir) - 1] !== DIRECTORY_SEPARATOR) {
$dir .= DIRECTORY_SEPARATOR;
}
if (0 === strpos($filename, $dir)) {
return true;
}
}
return false;
}
/**
* @return bool
*/
public static function isWindows()
{
return PATH_SEPARATOR === ';';
}
/**
* @param $key
*
* @return bool|mixed
*/
public static function envNotEmpty($key)
{
$value = self::env($key, false);
if ($value) {
return $value;
}
return false;
}
/**
* Gets the value of an environment variable.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public static function env($key, $default = null)
{
$value = getenv($key);
if ($value === false) {
return self::value($default);
}
if (self::envSubstr($value)) {
return substr($value, 1, -1);
}
return self::envConversion($value);
}
/**
* Return the default value of the given value.
*
* @param mixed $value
*
* @return mixed
*/
public static function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
/**
* @param $value
*
* @return bool
*/
public static function envSubstr($value)
{
return ($valueLength = strlen($value)) > 1
&& strpos($value, '"') === 0
&& $value[$valueLength - 1] === '"';
}
/**
* @param $value
*
* @return bool|string|null
*/
public static function envConversion($value)
{
$key = strtolower($value);
if ($key === 'null' || $key === '(null)') {
return null;
}
$list = [
'true' => true,
'(true)' => true,
'false' => false,
'(false)' => false,
'empty' => '',
'(empty)' => '',
];
return isset($list[$key]) ? $list[$key] : $value;
}
/**
* Gets the environment's HOME directory.
*
* @return null|string
*/
public static function getHomeDirectory()
{
if (getenv('HOME')) {
return getenv('HOME');
}
return (getenv('HOMEDRIVE') && getenv('HOMEPATH'))
? getenv('HOMEDRIVE') . getenv('HOMEPATH')
: null;
}
/**
* @param mixed ...$parameters
*
* @codeCoverageIgnore
*/
public static function dd(...$parameters)
{
dump(...$parameters);
exit;
}
/**
* Snake to camel case.
*
* @param string $str
*
* @return string
*/
public static function snakeToCamelCase($str)
{
$components = explode('_', $str);
$camelCaseStr = $components[0];
for ($i = 1; $i < count($components); $i++) {
$camelCaseStr .= ucfirst($components[$i]);
}
return $camelCaseStr;
}
/**
* Get user agent.
*
* @param string $userAgent
*
* @return string
*/
public static function getUserAgent()
{
return sprintf('AlibabaCloud (%s; %s) PHP/%s Credentials/%s TeaDSL/1', PHP_OS, \PHP_SAPI, PHP_VERSION, Credential::VERSION);
}
/**
* @param array $arrays
* @param string $key
*
* @return mix
*/
public static function unsetReturnNull(array $arrays, $key)
{
if(isset($arrays[$key])) {
return $arrays[$key];
}
return null;
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
namespace AlibabaCloud\Credentials\Utils;
use Exception;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Trait MockTrait
*
* @package AlibabaCloud\Credentials\Utils
*/
trait MockTrait
{
/**
* @var array
*/
private static $mockQueue = [];
/**
* @var array
*/
private static $history = [];
/**
* @var MockHandler
*/
private static $mock;
/**
* @param integer $status
* @param array $headers
* @param array|string|object $body
*/
public static function mockResponse($status = 200, array $headers = [], $body = null)
{
if (is_array($body) || is_object($body)) {
$body = json_encode($body);
}
self::$mockQueue[] = new Response($status, $headers, $body);
self::createHandlerStack();
}
private static function createHandlerStack()
{
self::$mock = new MockHandler(self::$mockQueue);
}
/**
* @return MockHandler
*/
public static function getHandlerHistory()
{
return Middleware::history(self::$history);
}
/**
* @param string $message
* @param RequestInterface $request
* @param ResponseInterface|null $response
* @param Exception|null $previous
* @param array $handlerContext
*/
public static function mockRequestException(
$message,
RequestInterface $request,
ResponseInterface $response = null,
Exception $previous = null,
array $handlerContext = []
) {
self::$mockQueue[] = new RequestException(
$message,
$request,
$response,
$previous,
$handlerContext
);
self::createHandlerStack();
}
/**
* @return void
*/
public static function cancelMock()
{
self::$mockQueue = [];
self::$mock = null;
}
/**
* @return bool
*/
public static function hasMock()
{
return (bool)self::$mockQueue;
}
/**
* @return MockHandler
*/
public static function getMock()
{
return self::$mock;
}
/**
* @return array
*/
public static function getHistroy()
{
return self::$history;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
/*
* This document has been generated with
* https://mlocati.github.io/php-cs-fixer-configurator/#version:2.15|configurator
* you can change this configuration by importing this file.
*/
return PhpCsFixer\Config::create()
->setRiskyAllowed(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__)
);
+86
View File
@@ -0,0 +1,86 @@
# CHANGELOG
## [1.0.4] - 2025-12-15
### Changed
- Improved PHP version compatibility for multi-version support (PHP 5.6 - 8.x)
- Refactored FileFormStream implementation with separate classes for different PHP versions
- Updated test environment to use alibabacloud.com domains
- Improved test assertions to verify HTTP status codes
- Updated all code comments to English
### Fixed
- Fixed PHP 7.0 compatibility issues with nullable types and void return types
- Fixed trait and interface return type conflicts
- Fixed test assertions for random values in RandomBackoffPolicy
- Fixed StreamUtil JSON parsing for HTML content
### Added
- Added FileFormStreamTrait for shared stream implementation
- Added FileFormStreamTyped for PHP 7.1+ with type declarations
- Added comprehensive test coverage improvements
## [1.0.3] - 2025-11-27
### Fixed
- Fixed psr/http-message dependency version constraint
- Locked psr/http-message to ^1.0 for PHP 5.5+ compatibility
### Changed
- Updated test domains from jsonplaceholder.typicode.com to alibabacloud.com
- Updated API endpoints to use api.alibabacloud.com
- Improved test validation to focus on HTTP status codes
## [1.0.2] - 2025-10-23
### Fixed
- Fixed StreamUtil methods for better stream handling
- Fixed stream reading and parsing functionality
### Added
- Added StreamUtilTest with comprehensive test coverage
### Changed
- Updated DaraRetryException.php (2025-07-30)
## [1.0.1] - 2025-06-12
### Changed
- Removed monolog dependency to reduce package size and simplify dependencies
- Refactored Console class to remove monolog dependency
- Improved console output handling with custom stream support
### Added
- Added ConsoleTest for comprehensive console functionality testing
## [1.0.0] - 2025-01-15
### Added
- Initial release of Alibaba Cloud Darabonba SDK for PHP
- Core SDK functionality for Alibaba Cloud Tea
- Request/Response handling with PSR-7 compatibility
- Model validation and serialization
- Retry policy with multiple backoff strategies:
- FixedBackoffPolicy
- RandomBackoffPolicy
- ExponentialBackoffPolicy
- EqualJitterBackoffPolicy
- FullJitterBackoffPolicy
- Exception handling:
- DaraException
- DaraRespException
- DaraRetryException
- DaraUnableRetryException
- Utility modules:
- Date and time utilities
- File operations support
- Stream utilities
- XML and form data handling
- URL manipulation
- String and byte utilities
- Math utilities
- Comprehensive test suite with PHPUnit
- CI/CD integration with GitHub Actions
- Code style configuration with PHP CS Fixer
- Support for PHP 5.5+
- Guzzle HTTP client integration (^6.3|^7.0)
+13
View File
@@ -0,0 +1,13 @@
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+27
View File
@@ -0,0 +1,27 @@
![](https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg)
## Alibaba Cloud Tea for Java
[![CI](https://github.com/aliyun/tea-php/actions/workflows/ci.yml/badge.svg)](https://github.com/aliyun/tea-php/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/aliyun/tea-php/branch/master/graph/badge.svg)](https://codecov.io/gh/aliyun/tea-php)
[![Latest Stable Version](https://poser.pugx.org/alibabacloud/tea/v/stable)](https://packagist.org/packages/alibabacloud/tea)
[![License](https://poser.pugx.org/alibabacloud/tea/license)](https://packagist.org/packages/alibabacloud/tea)
## Installation
```sh
composer require alibabacloud/tea --optimize-autoloader
```
> Some users may not be able to install due to network problems, you can try to switch the Composer mirror.
## Changelog
Detailed changes for each release are documented in the [release notes](CHANGELOG.md).
## License
[Apache-2.0](LICENSE.md)
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
+77
View File
@@ -0,0 +1,77 @@
{
"name": "alibabacloud/darabonba",
"homepage": "https://www.alibabacloud.com/",
"description": "Client of Darabonba for PHP",
"keywords": [
"tea",
"client",
"alibabacloud",
"cloud"
],
"type": "library",
"license": "Apache-2.0",
"support": {
"source": "https://github.com/aliyun/tea-php",
"issues": "https://github.com/aliyun/tea-php/issues"
},
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com",
"homepage": "http://www.alibabacloud.com"
}
],
"require": {
"php": ">=5.5",
"ext-curl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"adbario/php-dot-notation": "^2.4",
"alibabacloud/tea": "^3.2",
"guzzlehttp/guzzle": "^6.3|^7.0"
},
"require-dev": {
"symfony/dotenv": "^3.4",
"phpunit/phpunit": "^4.8.35|^5.4.3|^9.3",
"symfony/var-dumper": "^3.4"
},
"suggest": {
"ext-sockets": "To use client-side monitoring"
},
"autoload": {
"psr-4": {
"AlibabaCloud\\Dara\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"AlibabaCloud\\Dara\\Tests\\": "tests"
}
},
"config": {
"sort-packages": true,
"preferred-install": "dist",
"optimize-autoloader": true
},
"prefer-stable": true,
"minimum-stability": "dev",
"scripts": {
"cs": "phpcs --standard=PSR2 -n ./",
"cbf": "phpcbf --standard=PSR2 -n ./",
"fixer": "php-cs-fixer fix ./",
"unit": [
"@clearCache",
"XDEBUG_MODE=coverage phpunit --testsuite=Unit --colors=always --coverage-xml ./coverage/xml --coverage-html ./coverage/html --coverage-clover ./coverage/coverage.clover"
],
"feature": [
"@clearCache",
"phpunit --testsuite=Feature --colors=always"
],
"clearCache": "rm -rf cache/*",
"coverage": "open cache/coverage/index.html"
}
}
+372
View File
@@ -0,0 +1,372 @@
<?php
namespace AlibabaCloud\Dara;
use Adbar\Dot;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\RetryOptions;
use AlibabaCloud\Dara\RetryPolicy\RetryPolicyContext;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\RequestOptions;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
/**
* Class Dara.
*/
class Dara
{
/**
* @var array
*/
private static $config = [];
const MAX_DELAY_TIME = 120 * 1000;
const MIN_DELAY_TIME = 100;
public static function config(array $config)
{
self::$config = $config;
}
/**
* @throws GuzzleException
*
* @return Response
*/
public static function send(Request $request, array $config = [])
{
if (method_exists($request, 'getPsrRequest')) {
$request = $request->getPsrRequest();
}
$config = self::resolveConfig($config);
$res = self::client()->send(
$request,
$config
);
return new Response($res);
}
/**
* @return PromiseInterface
*/
public static function sendAsync(RequestInterface $request, array $config = [])
{
if (method_exists($request, 'getPsrRequest')) {
$request = $request->getPsrRequest();
}
$config = self::resolveConfig($config);
return self::client()->sendAsync(
$request,
$config
);
}
/**
* @return Client
*/
public static function client(array $config = [])
{
if (isset(self::$config['handler'])) {
$stack = self::$config['handler'];
} else {
$stack = HandlerStack::create();
$stack->push(Middleware::mapResponse(static function (ResponseInterface $response) {
return new Response($response);
}));
}
self::$config['handler'] = $stack;
if (!isset(self::$config['on_stats'])) {
self::$config['on_stats'] = function (TransferStats $stats) {
Response::$info = $stats->getHandlerStats();
};
}
$new_config = Helper::merge([self::$config, $config]);
return new Client($new_config);
}
/**
* @param string $method
* @param string|UriInterface $uri
* @param array $options
*
* @throws GuzzleException
*
* @return ResponseInterface
*/
public static function request($method, $uri, $options = [])
{
return self::client()->request($method, $uri, $options);
}
/**
* @param string $method
* @param string $uri
* @param array $options
*
* @throws GuzzleException
*
* @return string
*/
public static function string($method, $uri, $options = [])
{
return (string) self::client()->request($method, $uri, $options)
->getBody();
}
/**
* @param string $method
* @param string|UriInterface $uri
* @param array $options
*
* @return PromiseInterface
*/
public static function requestAsync($method, $uri, $options = [])
{
return self::client()->requestAsync($method, $uri, $options);
}
/**
* @param string|UriInterface $uri
* @param array $options
*
* @throws GuzzleException
*
* @return null|mixed
*/
public static function getHeaders($uri, $options = [])
{
return self::request('HEAD', $uri, $options)->getHeaders();
}
/**
* @param string|UriInterface $uri
* @param string $key
* @param null|mixed $default
*
* @throws GuzzleException
*
* @return null|mixed
*/
public static function getHeader($uri, $key, $default = null)
{
$headers = self::getHeaders($uri);
return isset($headers[$key][0]) ? $headers[$key][0] : $default;
}
/**
* @param int $retryTimes
* @param float $now
*
* @return bool
*/
public static function allowRetry(array $runtime, $retryTimes, $now)
{
unset($now);
if (!isset($retryTimes) || null === $retryTimes || !\is_numeric($retryTimes)) {
return false;
}
if ($retryTimes > 0 && (empty($runtime) || !isset($runtime['retryable']) || !$runtime['retryable'] || !isset($runtime['maxAttempts']))) {
return false;
}
$maxAttempts = $runtime['maxAttempts'];
$retry = empty($maxAttempts) ? 0 : (int) $maxAttempts;
return $retry >= $retryTimes;
}
/**
* @param int $retryTimes
*
* @return int
*/
public static function getBackoffTime(array $runtime, $retryTimes)
{
$backOffTime = 0;
$policy = isset($runtime['policy']) ? $runtime['policy'] : '';
if (empty($policy) || 'no' == $policy) {
return $backOffTime;
}
$period = isset($runtime['period']) ? $runtime['period'] : '';
if (null !== $period && '' !== $period) {
$backOffTime = (int) $period;
if ($backOffTime <= 0) {
return $retryTimes;
}
}
return $backOffTime;
}
public static function sleep($time)
{
sleep($time);
}
public static function isRetryable($retry, $retryTimes = 0)
{
if ($retry instanceof DaraException) {
return true;
}
if (\is_array($retry)) {
$max = isset($retry['maxAttempts']) ? (int) ($retry['maxAttempts']) : 3;
return $retryTimes <= $max;
}
return false;
}
/**
*
* @param RetryOptions $options
* @param RetryPolicyContext $optctxions
* @return bool
*/
public static function shouldRetry($options, $ctx) {
if($ctx->getRetryCount() === 0) {
return true;
}
if (!$options || !$options->getRetryable()) {
return false;
}
$retriesAttempted = $ctx->getRetryCount();
$ex = $ctx->getException();
$conditions = $options->getNoRetryCondition();
foreach ($conditions as $condition) {
if (in_array($ex->getName(), $condition->getException()) || in_array($ex->getErrCode(), $condition->getErrorCode())) {
return false;
}
}
$conditions = $options->getRetryCondition();
foreach ($conditions as $condition) {
if (!in_array($ex->getName(), $condition->getException()) && !in_array($ex->getErrCode(), $condition->getErrorCode())) {
continue;
}
if ($retriesAttempted >= $condition->getMaxAttempts()) {
return false;
}
return true;
}
return false;
}
/**
*
* @param RetryOptions $options
* @param RetryPolicyContext $optctxions
* @return int
*/
public static function getBackoffDelay($options, $ctx) {
$ex = $ctx->getException();
$fullClassName = get_class($ex);
$classNameParts = explode('\\', $fullClassName);
$className = end($classNameParts);
$conditions = $options->getRetryCondition();
foreach ($conditions as $condition) {
if (!in_array($className, $condition->getException()) && !in_array($ex->getErrCode(), $condition->getErrorCode())) {
continue;
}
$maxDelay = $condition->getMaxDelay() ?: self::MAX_DELAY_TIME;
$retryAfter = method_exists($ex, 'getRetryAfter') ? $ex->getRetryAfter() : null;
if ($retryAfter !== null) {
return min($retryAfter, $maxDelay);
}
$backoff = $condition->getBackoff();
if (!isset($backoff) || null === $backoff) {
return self::MIN_DELAY_TIME;
}
return min($backoff->getDelayTime($ctx), $maxDelay);
}
return self::MIN_DELAY_TIME;
}
/**
* @param mixed|Model[] ...$item
*
* @return mixed
*/
public static function merge(...$item)
{
$tmp = [];
$n = 0;
foreach ($item as $i) {
if (\is_object($i)) {
if ($i instanceof Model) {
$i = $i->toMap();
} else {
$i = json_decode(json_encode($i), true);
}
}
if (null === $i) {
continue;
}
if (\is_array($i)) {
$tmp[$n++] = $i;
}
}
if (\count($tmp)) {
return \call_user_func_array('array_merge', $tmp);
}
return [];
}
private static function resolveConfig(array $config = [])
{
$options = new Dot(['http_errors' => false]);
if (isset($config['httpProxy']) && !empty($config['httpProxy'])) {
$options->set('proxy.http', $config['httpProxy']);
}
if (isset($config['httpsProxy']) && !empty($config['httpsProxy'])) {
$options->set('proxy.https', $config['httpsProxy']);
}
if (isset($config['noProxy']) && !empty($config['noProxy'])) {
$options->set('proxy.no', $config['noProxy']);
}
if (isset($config['ignoreSSL']) && !empty($config['ignoreSSL'])) {
$options->set('verify',!((bool)$config['ignoreSSL']));
}
if (isset($config['stream']) && !empty($config['stream'])) {
$options->set(RequestOptions::STREAM, (bool)$config['stream']);
}
// readTimeout&connectTimeout unit is millisecond
$read_timeout = isset($config['readTimeout']) && !empty($config['readTimeout']) ? (int) $config['readTimeout'] : 3000;
$con_timeout = isset($config['connectTimeout']) && !empty($config['connectTimeout']) ? (int) $config['connectTimeout'] : 3000;
// timeout unit is second
$options->set('timeout', ($read_timeout + $con_timeout) / 1000);
return $options->all();
}
}
+148
View File
@@ -0,0 +1,148 @@
<?php
namespace AlibabaCloud\Dara;
use DateTime;
use DateTimeZone;
use DateInterval;
use AlibabaCloud\Dara\Exception\DaraException;
class Date
{
private $date = null;
public function __construct($date = 'now') {
$pattern = '/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?)(?: \+?(\d{4}))?/';
if (ctype_digit($date) || is_numeric($date)) {
$this->date = (new DateTime())->setTimestamp($date);
} elseif (preg_match($pattern, $date, $matches)) {
$timeStr = $matches[1];
$tzStr = isset($matches[2]) ? $matches[2] : null;
if ($tzStr) {
$timezone = new DateTimeZone($this->convertTzOffsetToTzString($tzStr));
$this->date = DateTime::createFromFormat('Y-m-d H:i:s.u', $timeStr, $timezone);
} else {
$this->date = new DateTime($timeStr);
}
} else {
$this->date = new DateTime($date);
}
if($this->date === false || is_null($this->date)) {
throw new DaraException([], $date . ' is not a valid time str.');
}
}
private function convertTzOffsetToTzString($offset) {
$sign = (intval($offset) >= 0) ? '+' : '-';
$hours = substr($offset, 0, 2);
$minutes = substr($offset, 2, 2);
return $sign . $hours . ':' . $minutes;
}
public function format($layout) {
$layout = strtr($layout, [
'yyyy' => 'Y', 'yy' => 'y',
'MM' => 'm', 'M' => 'n',
'DD' => 'd', 'D' => 'j',
'HH' => 'H', 'H' => 'G',
'hh' => 'h', 'h' => 'g',
'mm' => 'i', 'm' => 'i',
'ss' => 's', 's' => 's',
'A' => 'A', 'a' => 'a',
'E' => 'N', 'YYYY' => 'Y',
]);
return $this->date->format($layout);
}
public function UTC($time = null)
{
$utcDate = clone $this->date;
$utcDate->setTimezone(new DateTimeZone('UTC'));
return $utcDate->format('Y-m-d H:i:s.u O \\U\\T\\C');
}
public function unix() {
$date = $this->date;
return $date->getTimestamp();
}
public function sub($unit, $amount) {
$interval = new DateInterval('P' . strtoupper($amount) . strtoupper((string)$unit));
$this->date->sub($interval);
return $this;
}
public function add($unit, $amount) {
$interval = new DateInterval('P' . strtoupper($amount) . strtoupper((string)$unit));
$this->date->add($interval);
return $this;
}
public function diff($diffDate, $unit = null) {
$interval = $this->date->diff($diffDate->getDateObject());
switch ($unit) {
case 'year':
return $interval->y;
case 'month':
return $interval->m;
case 'day':
return $interval->d;
case 'hour':
return $interval->h;
case 'minute':
return $interval->i;
case 'second':
return $interval->s;
default:
return ($interval->days * 24 * 60 * 60) +
($interval->h * 60 * 60) +
($interval->i * 60) +
$interval->s;
}
}
public function hour() {
return (int)$this->date->format('H');
}
public function minute() {
return (int)$this->date->format('i');
}
public function second() {
return (int)$this->date->format('s');
}
public function month() {
return (int)$this->date->format('n');
}
public function year() {
return (int)$this->date->format('Y');
}
public function dayOfMonth() {
return (int)$this->date->format('j');
}
public function dayOfWeek() {
$weekday = (int)$this->date->format('w');
return $weekday === 0 ? 7 : $weekday;
}
public function weekOfYear() {
$week = (int)$this->date->format('W');
$weekday = (int)$this->date->format('w');
if ($weekday === 0 && $this->date->format('z') === (string)($this->date->format('L') ? '365' : '364')) {
return (int)$this->date->sub(new DateInterval('P1D'))->format('W');
}
return $week;
}
public function getDateObject() {
return clone $this->date;
}
}
@@ -0,0 +1,67 @@
<?php
namespace AlibabaCloud\Dara\Exception;
use AlibabaCloud\Tea\Exception\TeaError;
/**
* Class DaraException.
*/
class DaraException extends TeaError
{
public $message = '';
public $errCode = '';
public $data;
public $name = '';
public $statusCode;
public $description;
public $accessDeniedDetail;
public $errorInfo;
/**
* DaraError DaraException.
*
* @param array $errorInfo
* @param string $message
* @param int $code
* @param null|\Throwable $previous
*/
public function __construct($errorInfo = [], $message = '', $code = '', $previous = null)
{
parent::__construct($errorInfo, $message, $code, $previous);
$this->errorInfo = $errorInfo;
$this->name = 'BaseError';
if (!empty($errorInfo)) {
$properties = ['name', 'message', 'errCode', 'data', 'description', 'accessDeniedDetail'];
foreach ($properties as $property) {
if (isset($errorInfo[$property])) {
$this->{$property} = $errorInfo[$property];
}
}
}
}
/**
* @return array
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getErrCode()
{
return $this->errCode;
}
/**
* @return array
*/
public function getErrorInfo()
{
return $this->errorInfo;
}
}
@@ -0,0 +1,88 @@
<?php
namespace AlibabaCloud\Dara\Exception;
/**
* Class DaraRespException.
*/
class DaraRespException extends DaraException
{
public $statusCode;
protected $retryAfter;
public $data;
public $accessDeniedDetail;
public $description;
/**
* DaraRespException constructor.
*
* @param array $errorInfo
* @param string $message
* @param int $code
* @param null|\Throwable $previous
*/
public function __construct($errorInfo = [], $message = '', $code = 0, $previous = null)
{
parent::__construct($errorInfo, (string) $message, (int) $code, $previous);
$this->name = 'ResponseError';
if (!empty($errorInfo)) {
$properties = ['retryAfter', 'statusCode', 'data', 'description', 'accessDeniedDetail'];
foreach ($properties as $property) {
if (isset($errorInfo[$property])) {
$this->{$property} = $errorInfo[$property];
if ($property === 'data' && isset($errorInfo['data']['statusCode'])) {
$this->statusCode = $errorInfo['data']['statusCode'];
}
}
}
}
}
/**
* @return array
*/
public function getErrorInfo()
{
return $this->errorInfo;
}
/**
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @return int
*/
public function getRetryAfter()
{
return $this->retryAfter;
}
/**
* @return array
*/
public function getAccessDeniedDetail()
{
return $this->accessDeniedDetail;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @return array
*/
public function getData()
{
return $this->data;
}
}
@@ -0,0 +1,21 @@
<?php
namespace AlibabaCloud\Dara\Exception;
/**
* Class DaraRetryException
*/
class DaraRetryException extends DaraException
{
/**
* DaraRetryException constructor.
*
* @param string $message
* @param int $code
* @param null|\Throwable $previous
*/
public function __construct($message = '', $code = 0, $previous = null)
{
parent::__construct([], $message, $code, $previous);
}
}
@@ -0,0 +1,46 @@
<?php
namespace AlibabaCloud\Dara\Exception;
use AlibabaCloud\Dara\Request;
use AlibabaCloud\Dara\RetryPolicy\RetryPolicyContext;
/**
* Class DaraUnableRetryException.
*/
class DaraUnableRetryException extends DaraException
{
private $lastRequest;
private $lastException;
/**
* DaraUnableRetryException constructor.
*
* @param Request $lastRequest
* @param null|\Exception $lastException
*/
public function __construct($lastRequest, $lastException = null)
{
if($lastRequest instanceof RetryPolicyContext) {
$lastException = $lastRequest->getException();
$lastRequest = $lastRequest->getHttpRequest();
}
$error_info = [];
if (null !== $lastException && $lastException instanceof DaraException) {
$error_info = $lastException->getErrorInfo();
}
parent::__construct($error_info, $lastException->getMessage(), $lastException->getCode(), $lastException);
$this->lastRequest = $lastRequest;
$this->lastException = $lastException;
}
public function getLastRequest()
{
return $this->lastRequest;
}
public function getLastException()
{
return $this->lastException;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace AlibabaCloud\Dara;
use AlibabaCloud\Dara\Date;
use GuzzleHttp\Psr7\Stream;
use GuzzleHttp\Psr7\Utils;
use AlibabaCloud\Dara\Exception\DaraException;
class File
{
private $_path;
private $_stat = null;
private $_fd = false;
private $_position = 0;
public function __construct($path) {
$this->_path = $path;
}
public function path() {
return $this->_path;
}
public function createTime() {
if (!$this->_stat) {
$this->_stat = stat($this->_path);
}
return new Date($this->_stat['ctime']);
}
public function modifyTime() {
if (!$this->_stat) {
$this->_stat = stat($this->_path);
}
return new Date($this->_stat['mtime']);
}
public function length() {
if (!$this->_stat) {
$this->_stat = stat($this->_path);
}
return $this->_stat['size'];
}
public function read($size) {
if (!$this->_fd) {
$this->_fd = fopen($this->_path, 'a+');
}
$position = ftell($this->_fd);
$position = ftell($this->_fd);
fseek($this->_fd, $this->_position);
$data = fread($this->_fd, $size);
$bytesRead = strlen($data);
if (!$bytesRead) {
return null;
}
$this->_position += $bytesRead;
return $data;
}
public function write($data) {
if (!$this->_fd) {
$this->_fd = fopen($this->_path, 'a+');
}
fwrite($this->_fd, $data);
fflush($this->_fd);
clearstatcache();
$this->_stat = stat($this->_path);
}
public function close() {
if ($this->_fd) {
fclose($this->_fd);
$this->_fd = false;
}
}
/**
*
* @param string $path
* @return bool
*/
public static function exists($path) {
return file_exists($path);
}
/**
*
* @param string $path
* @return Stream
*/
public static function createReadStream($path) {
try {
$stream = Utils::streamFor(fopen($path, 'r'));
return $stream;
} catch (Exception $e) {
throw new DaraException([], "Unable to open file for reading: " . $e->getMessage());
}
}
/**
*
* @param string $path
* @return Stream
*/
public static function createWriteStream($path) {
try {
$stream = Utils::streamFor(fopen($path, 'a+'));
return $stream;
} catch (Exception $e) {
throw new DaraException([], "Unable to open file for writing: " . $e->getMessage());
}
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
namespace AlibabaCloud\Dara;
class Helper
{
/**
* @param string $content
* @param string $prefix
* @param string $end
* @param string[] $filter
*
* @return string|string[]
*/
public static function findFromString($content, $prefix, $end, $filter = ['"', ' '])
{
$len = mb_strlen($prefix);
$pos = mb_strpos($content, $prefix);
if (false === $pos) {
return '';
}
$pos_end = mb_strpos($content, $end, $pos);
$str = mb_substr($content, $pos + $len, $pos_end - $pos - $len);
return str_replace($filter, '', $str);
}
/**
* @param string $str
*
* @return bool
*/
public static function isJson($str)
{
json_decode($str);
return \JSON_ERROR_NONE == json_last_error();
}
/**
* @param mixed $value
*
* @return bool
*/
public static function isBytes($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;
}
/**
* Convert a bytes to string(utf8).
*
* @param array $bytes
*
* @return string the return string
*/
public static function toString($bytes)
{
$str = '';
foreach ($bytes as $ch) {
$str .= \chr($ch);
}
return $str;
}
/**
* @return array
*/
public static function merge(array $arrays)
{
$result = [];
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (\is_int($key)) {
$result[] = $value;
continue;
}
if (isset($result[$key]) && \is_array($result[$key])) {
$result[$key] = self::merge(
[$result[$key], $value]
);
continue;
}
$result[$key] = $value;
}
}
return $result;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace AlibabaCloud\Dara;
class Model
{
protected $_name = [];
protected $_required = [];
public function __construct($config = [])
{
if (!empty($config)) {
foreach ($config as $k => $v) {
$this->{$k} = $v;
}
}
}
public function getName($name = null)
{
if (null === $name) {
return $this->_name;
}
return isset($this->_name[$name]) ? $this->_name[$name] : $name;
}
public function toMap()
{
$map = get_object_vars($this);
foreach ($map as $k => $m) {
if (0 === strpos($k, '_')) {
unset($map[$k]);
}
}
$res = [];
foreach ($map as $k => $v) {
$name = isset($this->_name[$k]) ? $this->_name[$k] : $k;
$res[$name] = $v;
}
return $res;
}
public function validate()
{
$vars = get_object_vars($this);
foreach ($vars as $k => $v) {
if (isset($this->_required[$k]) && $this->_required[$k] && empty($v)) {
throw new \InvalidArgumentException("{$k} is required.");
}
}
}
public function copyWithoutStream() {
$map = $this->toArray(true);
$calledClass = get_called_class();
if (method_exists($calledClass, 'fromMap')) {
return $calledClass::fromMap($map);
}
return null;
}
public static function validateRequired($fieldName, $field, $val = null)
{
if (true === $val && null === $field) {
throw new \InvalidArgumentException($fieldName . ' is required');
}
}
public static function validateMaxLength($fieldName, $field, $val = null)
{
if (null !== $field && \strlen($field) > (int) $val) {
throw new \InvalidArgumentException($fieldName . ' is exceed max-length: ' . $val);
}
}
public static function validateMinLength($fieldName, $field, $val = null)
{
if (null !== $field && \strlen($field) < (int) $val) {
throw new \InvalidArgumentException($fieldName . ' is less than min-length: ' . $val);
}
}
public static function validatePattern($fieldName, $field, $regex = '')
{
if (null !== $field && '' !== $field && !preg_match("/^{$regex}$/", $field)) {
throw new \InvalidArgumentException($fieldName . ' is not match ' . $regex);
}
}
public static function validateMaximum($fieldName, $field, $val)
{
if (null !== $field && $field > $val) {
throw new \InvalidArgumentException($fieldName . ' cannot be greater than ' . $val);
}
}
public static function validateMinimum($fieldName, $field, $val)
{
if (null !== $field && $field < $val) {
throw new \InvalidArgumentException($fieldName . ' cannot be less than ' . $val);
}
}
public static function validateArray($arr)
{
if (null === $arr) {
return;
}
foreach($arr as $item) {
if($item instanceof Model) {
$item->validate();
} else if(is_array($item)){
self::validateArray($item);
}
}
}
/**
* @param array $map
* @param Model $model
*
* @return mixed
*/
public static function toModel($map, $model)
{
$names = $model->getName();
$names = array_flip($names);
foreach ($map as $key => $value) {
$name = isset($names[$key]) ? $names[$key] : $key;
$model->{$name} = $value;
}
return $model;
}
}
@@ -0,0 +1,39 @@
<?php
// This file is auto-generated, don't edit it. Thanks.
namespace AlibabaCloud\Dara\Models;
use AlibabaCloud\Dara\Model;
class ExtendsParameters extends Model {
public $headers;
public $queries;
public function validate() {}
public function toMap() {
$res = [];
if (null !== $this->headers) {
$res['headers'] = $this->headers;
}
if (null !== $this->queries) {
$res['queries'] = $this->queries;
}
return $res;
}
/**
* @param array $map
* @return ExtendsParameters
*/
public static function fromMap($map = []) {
$model = new self();
if(isset($map['headers'])){
$model->headers = $map['headers'];
}
if(isset($map['queries'])){
$model->queries = $map['queries'];
}
return $model;
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace AlibabaCloud\Dara\Models;
use AlibabaCloud\Dara\Model;
class FileField extends Model
{
public $filename;
public $contentType;
public $content;
public function __construct($config = [])
{
$this->_required = [
'filename' => true,
'contentType' => true,
'content' => true,
];
parent::__construct($config);
}
}
@@ -0,0 +1,273 @@
<?php
// This file is auto-generated, don't edit it. Thanks.
namespace AlibabaCloud\Dara\Models;
use AlibabaCloud\Dara\Model;
/**
* The common runtime options model
*/
class RuntimeOptions extends Model {
protected $_name = [
'autoretry' => 'autoretry',
'ignoreSSL' => 'ignoreSSL',
'key' => 'key',
'cert' => 'cert',
'ca' => 'ca',
'maxAttempts' => 'max_attempts',
'backoffPolicy' => 'backoff_policy',
'backoffPeriod' => 'backoff_period',
'readTimeout' => 'readTimeout',
'connectTimeout' => 'connectTimeout',
'httpProxy' => 'httpProxy',
'httpsProxy' => 'httpsProxy',
'noProxy' => 'noProxy',
'maxIdleConns' => 'maxIdleConns',
'localAddr' => 'localAddr',
'socks5Proxy' => 'socks5Proxy',
'socks5NetWork' => 'socks5NetWork',
'keepAlive' => 'keepAlive',
];
public function validate() {}
public function toMap() {
$res = [];
if (null !== $this->autoretry) {
$res['autoretry'] = $this->autoretry;
}
if (null !== $this->ignoreSSL) {
$res['ignoreSSL'] = $this->ignoreSSL;
}
if (null !== $this->key) {
$res['key'] = $this->key;
}
if (null !== $this->cert) {
$res['cert'] = $this->cert;
}
if (null !== $this->ca) {
$res['ca'] = $this->ca;
}
if (null !== $this->maxAttempts) {
$res['max_attempts'] = $this->maxAttempts;
}
if (null !== $this->backoffPolicy) {
$res['backoff_policy'] = $this->backoffPolicy;
}
if (null !== $this->backoffPeriod) {
$res['backoff_period'] = $this->backoffPeriod;
}
if (null !== $this->readTimeout) {
$res['readTimeout'] = $this->readTimeout;
}
if (null !== $this->connectTimeout) {
$res['connectTimeout'] = $this->connectTimeout;
}
if (null !== $this->httpProxy) {
$res['httpProxy'] = $this->httpProxy;
}
if (null !== $this->httpsProxy) {
$res['httpsProxy'] = $this->httpsProxy;
}
if (null !== $this->noProxy) {
$res['noProxy'] = $this->noProxy;
}
if (null !== $this->maxIdleConns) {
$res['maxIdleConns'] = $this->maxIdleConns;
}
if (null !== $this->localAddr) {
$res['localAddr'] = $this->localAddr;
}
if (null !== $this->socks5Proxy) {
$res['socks5Proxy'] = $this->socks5Proxy;
}
if (null !== $this->socks5NetWork) {
$res['socks5NetWork'] = $this->socks5NetWork;
}
if (null !== $this->keepAlive) {
$res['keepAlive'] = $this->keepAlive;
}
if (null !== $this->extendsParameters) {
$res['extendsParameters'] = null !== $this->extendsParameters ? $this->extendsParameters->toMap() : null;
}
return $res;
}
/**
* @param array $map
* @return RuntimeOptions
*/
public static function fromMap($map = []) {
$model = new self();
if(isset($map['autoretry'])){
$model->autoretry = $map['autoretry'];
}
if(isset($map['ignoreSSL'])){
$model->ignoreSSL = $map['ignoreSSL'];
}
if(isset($map['key'])){
$model->key = $map['key'];
}
if(isset($map['cert'])){
$model->cert = $map['cert'];
}
if(isset($map['ca'])){
$model->ca = $map['ca'];
}
if(isset($map['max_attempts'])){
$model->maxAttempts = $map['max_attempts'];
}
if(isset($map['backoff_policy'])){
$model->backoffPolicy = $map['backoff_policy'];
}
if(isset($map['backoff_period'])){
$model->backoffPeriod = $map['backoff_period'];
}
if(isset($map['readTimeout'])){
$model->readTimeout = $map['readTimeout'];
}
if(isset($map['connectTimeout'])){
$model->connectTimeout = $map['connectTimeout'];
}
if(isset($map['httpProxy'])){
$model->httpProxy = $map['httpProxy'];
}
if(isset($map['httpsProxy'])){
$model->httpsProxy = $map['httpsProxy'];
}
if(isset($map['noProxy'])){
$model->noProxy = $map['noProxy'];
}
if(isset($map['maxIdleConns'])){
$model->maxIdleConns = $map['maxIdleConns'];
}
if(isset($map['localAddr'])){
$model->localAddr = $map['localAddr'];
}
if(isset($map['socks5Proxy'])){
$model->socks5Proxy = $map['socks5Proxy'];
}
if(isset($map['socks5NetWork'])){
$model->socks5NetWork = $map['socks5NetWork'];
}
if(isset($map['keepAlive'])){
$model->keepAlive = $map['keepAlive'];
}
if(isset($map['extendsParameters'])){
$model->extendsParameters = ExtendsParameters::fromMap($map['extendsParameters']);
}
return $model;
}
/**
* @description whether to try again
* @var bool
*/
public $autoretry;
/**
* @description ignore SSL validation
* @var bool
*/
public $ignoreSSL;
/**
* @description privite key for client certificate
* @var string
*/
public $key;
/**
* @description client certificate
* @var string
*/
public $cert;
/**
* @description server certificate
* @var string
*/
public $ca;
/**
* @description maximum number of retries
* @var int
*/
public $maxAttempts;
/**
* @description backoff policy
* @var string
*/
public $backoffPolicy;
/**
* @description backoff period
* @var int
*/
public $backoffPeriod;
/**
* @description read timeout
* @var int
*/
public $readTimeout;
/**
* @description connect timeout
* @var int
*/
public $connectTimeout;
/**
* @description http proxy url
* @var string
*/
public $httpProxy;
/**
* @description https Proxy url
* @var string
*/
public $httpsProxy;
/**
* @description agent blacklist
* @var string
*/
public $noProxy;
/**
* @description maximum number of connections
* @var int
*/
public $maxIdleConns;
/**
* @description local addr
* @var string
*/
public $localAddr;
/**
* @description SOCKS5 proxy
* @var string
*/
public $socks5Proxy;
/**
* @description SOCKS5 netWork
* @var string
*/
public $socks5NetWork;
/**
* @description whether to enable keep-alive
* @var bool
*/
public $keepAlive;
/**
* @description Extends Parameters
* @var ExtendsParameters
*/
public $extendsParameters;
}
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace AlibabaCloud\Dara\Models;
use AlibabaCloud\Dara\Model;
class SSEEvent extends Model {
public $data;
public $id;
public $event;
public $retry;
public function __construct($data = array()) {
$this->data = isset($data['data']) ? $data['data'] : null;
$this->id = isset($data['id']) ? $data['id'] : null;
$this->event = isset($data['event']) ? $data['event'] : null;
$this->retry = isset($data['retry']) ? $data['retry'] : null;
}
public function validate() { }
public function toArray()
{
$res = [];
if (null !== $this->data) {
$res['data'] = $this->data;
}
if (null !== $this->id) {
$res['id'] = $this->id;
}
if (null !== $this->event) {
$res['event'] = $this->event;
}
if (null !== $this->retry) {
$res['retry'] = $this->retry;
}
return $res;
}
public function toMap()
{
return $this->toArray();
}
public static function fromMap($map = [])
{
$model = new self();
if (isset($map['data'])) {
if(!empty($map['data'])){
$model->data = [];
foreach($map['data'] as $key => $value) {
$model->data[$key] = $value;
}
}
}
if (isset($map['id'])) {
$model->id = $map['id'];
}
if (isset($map['event'])) {
$model->event = $map['event'];
}
if (isset($map['retry'])) {
$model->retry = $map['retry'];
}
return $res;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace AlibabaCloud\Dara;
use ArrayIterator;
use IteratorAggregate;
use ReflectionObject;
use Traversable;
/**
* Class Parameter.
*/
abstract class Parameter implements IteratorAggregate
{
/**
* @return ArrayIterator|Traversable
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->toArray());
}
/**
* @return array
*/
public function getRealParameters()
{
$array = [];
$obj = new ReflectionObject($this);
$properties = $obj->getProperties();
foreach ($properties as $property) {
$docComment = $property->getDocComment();
$key = trim(Helper::findFromString($docComment, '@real', "\n"));
$value = $property->getValue($this);
$array[$key] = $value;
}
return $array;
}
/**
* @return array
*/
public function toArray()
{
return $this->getRealParameters();
}
}
+123
View File
@@ -0,0 +1,123 @@
<?php
namespace AlibabaCloud\Dara;
use GuzzleHttp\Psr7\Request as PsrRequest;
use InvalidArgumentException;
use Psr\Http\Message\StreamInterface;
/**
* Class Request.
*/
class Request extends PsrRequest
{
/**
* @var string
*/
public $protocol = 'https';
/**
* @var string
*/
public $pathname = '/';
/**
* @var array
*/
public $headers = [];
/**
* @var array
*/
public $query = [];
/**
* @var string
*/
public $body;
/**
* @var int
*/
public $port;
public $method;
public function __construct($method = 'GET', $uri = '', array $headers = [], $body = null, $version = '1.1')
{
parent::__construct($method, $uri, $headers, $body, $version);
$this->method = $method;
}
/**
* These fields are compatible if you define other fields.
* Mainly for compatibility situations where the code generator cannot generate set properties.
*
* @return PsrRequest
*/
public function getPsrRequest()
{
$this->assertQuery($this->query);
$request = clone $this;
$uri = $request->getUri();
if ($this->query) {
$uri = $uri->withQuery(http_build_query($this->query));
}
if ($this->port) {
$uri = $uri->withPort($this->port);
}
if ($this->protocol) {
$uri = $uri->withScheme($this->protocol);
}
if ($this->pathname) {
$uri = $uri->withPath($this->pathname);
}
if (isset($this->headers['host'])) {
$uri = $uri->withHost($this->headers['host']);
}
$request = $request->withUri($uri);
$request = $request->withMethod($this->method);
if ('' !== $this->body && null !== $this->body) {
if ($this->body instanceof StreamInterface) {
$request = $request->withBody($this->body);
} else {
$body = $this->body;
if (Helper::isBytes($this->body)) {
$body = Helper::toString($this->body);
}
if (\function_exists('\GuzzleHttp\Psr7\stream_for')) {
// @deprecated stream_for will be removed in guzzlehttp/psr7:2.0
$request = $request->withBody(\GuzzleHttp\Psr7\stream_for($body));
} else {
$request = $request->withBody(\GuzzleHttp\Psr7\Utils::streamFor($body));
}
}
}
if ($this->headers) {
foreach ($this->headers as $key => $value) {
$request = $request->withHeader($key, $value);
}
}
return $request;
}
/**
* @param array $query
*/
private function assertQuery($query)
{
if (!\is_array($query) && $query !== null) {
throw new InvalidArgumentException('Query must be array.');
}
}
}
+379
View File
@@ -0,0 +1,379 @@
<?php
namespace AlibabaCloud\Dara;
use Adbar\Dot;
use ArrayAccess;
use Countable;
use GuzzleHttp\Psr7\Response as PsrResponse;
use GuzzleHttp\TransferStats;
use IteratorAggregate;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use AlibabaCloud\Dara\Util\StringUtil;
/**
* Class Response.
*/
class Response extends PsrResponse implements ArrayAccess, IteratorAggregate, Countable
{
public $headers = [];
public $statusCode;
public $statusMessage = '';
/**
* @var TransferStats
*/
public static $info;
/**
* @var StreamInterface
*/
public $body;
/**
* Instance of the Dot.
*
* @var Dot
*/
protected $dot;
/**
* Response constructor.
*/
public function __construct(ResponseInterface $response)
{
parent::__construct(
$response->getStatusCode(),
$response->getHeaders(),
$response->getBody(),
$response->getProtocolVersion(),
$response->getReasonPhrase()
);
$this->headers = $response->getHeaders();
$this->body = $response->getBody();
$this->statusCode = $response->getStatusCode();
if ($this->body->isSeekable()) {
$this->body->seek(0);
}
$contentType = $this->getHeaderLine('Content-Type');
if(StringUtil::hasPrefix($contentType, 'text/event-stream')) {
return;
}
if (Helper::isJson((string) $this->getBody())) {
$this->dot = new Dot($this->toArray());
} else {
$this->dot = new Dot();
}
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->getBody();
}
/**
* @param string $name
*
* @return null|mixed
*/
public function __get($name)
{
$data = $this->dot->all();
if (!isset($data[$name])) {
return null;
}
return json_decode(json_encode($data))->{$name};
}
/**
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$this->dot->set($name, $value);
}
/**
* @param string $name
*
* @return bool
*/
public function __isset($name)
{
return $this->dot->has($name);
}
/**
* @param $offset
*/
public function __unset($offset)
{
$this->dot->delete($offset);
}
/**
* @return array
*/
public function toArray()
{
return \GuzzleHttp\json_decode((string) $this->getBody(), true);
}
/**
* @param array|int|string $keys
* @param mixed $value
*/
public function add($keys, $value = null)
{
return $this->dot->add($keys, $value);
}
/**
* @return array
*/
public function all()
{
return $this->dot->all();
}
/**
* @param null|array|int|string $keys
*/
public function clear($keys = null)
{
return $this->dot->clear($keys);
}
/**
* @param array|int|string $keys
*/
public function delete($keys)
{
return $this->dot->delete($keys);
}
/**
* @param string $delimiter
* @param null|array $items
* @param string $prepend
*
* @return array
*/
public function flatten($delimiter = '.', $items = null, $prepend = '')
{
return $this->dot->flatten($delimiter, $items, $prepend);
}
/**
* @param null|int|string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key = null, $default = null)
{
return $this->dot->get($key, $default);
}
/**
* @param array|int|string $keys
*
* @return bool
*/
public function has($keys)
{
return $this->dot->has($keys);
}
/**
* @param null|array|int|string $keys
*
* @return bool
*/
public function isEmpty($keys = null)
{
return $this->dot->isEmpty($keys);
}
/**
* @param array|self|string $key
* @param array|self $value
*/
public function merge($key, $value = [])
{
return $this->dot->merge($key, $value);
}
/**
* @param array|self|string $key
* @param array|self $value
*/
public function mergeRecursive($key, $value = [])
{
return $this->dot->mergeRecursive($key, $value);
}
/**
* @param array|self|string $key
* @param array|self $value
*/
public function mergeRecursiveDistinct($key, $value = [])
{
return $this->dot->mergeRecursiveDistinct($key, $value);
}
/**
* @param null|int|string $key
* @param mixed $default
*
* @return mixed
*/
public function pull($key = null, $default = null)
{
return $this->dot->pull($key, $default);
}
/**
* @param null|int|string $key
* @param mixed $value
*
* @return mixed
*/
public function push($key = null, $value = null)
{
return $this->dot->push($key, $value);
}
/**
* Replace all values or values within the given key
* with an array or Dot object.
*
* @param array|self|string $key
* @param array|self $value
*/
public function replace($key, $value = [])
{
return $this->dot->replace($key, $value);
}
/**
* Set a given key / value pair or pairs.
*
* @param array|int|string $keys
* @param mixed $value
*/
public function set($keys, $value = null)
{
return $this->dot->set($keys, $value);
}
/**
* Replace all items with a given array.
*
* @param mixed $items
*/
public function setArray($items)
{
return $this->dot->setArray($items);
}
/**
* Replace all items with a given array as a reference.
*/
public function setReference(array &$items)
{
return $this->dot->setReference($items);
}
/**
* Return the value of a given key or all the values as JSON.
*
* @param mixed $key
* @param int $options
*
* @return string
*/
public function toJson($key = null, $options = 0)
{
return $this->dot->toJson($key, $options);
}
/**
* Retrieve an external iterator.
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return $this->dot->getIterator();
}
/**
* Whether a offset exists.
*
* @param $offset
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return $this->dot->offsetExists($offset);
}
/**
* Offset to retrieve.
*
* @param $offset
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->dot->offsetGet($offset);
}
/**
* Offset to set.
*
* @param $offset
* @param $value
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->dot->offsetSet($offset, $value);
}
/**
* Offset to unset.
*
* @param $offset
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->dot->offsetUnset($offset);
}
/**
* Count elements of an object.
*
* @param null $key
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count($key = null)
{
return $this->dot->count($key);
}
}
@@ -0,0 +1,46 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\EqualJitterBackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\ExponentialBackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\FixedBackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\FullJitterBackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\RandomBackoffPolic;
interface BackoffPolicyInterface {
public function getDelayTime($ctx);
}
abstract class BackoffPolicy implements BackoffPolicyInterface {
protected $policy;
public function __construct($option) {
$this->policy = $option['policy'];
}
abstract public function getDelayTime($ctx);
public static function newBackoffPolicy($option) {
switch($option['policy']) {
case 'Fixed':
return new FixedBackoffPolicy($option);
case 'Random':
return new RandomBackoffPolicy($option);
case 'Exponential':
return new ExponentialBackoffPolicy($option);
case 'EqualJitter':
case 'ExponentialWithEqualJitter':
return new EqualJitterBackoffPolicy($option);
case 'FullJitter':
case 'ExponentialWithFullJitter':
return new FullJitterBackoffPolicy($option);
default:
throw new DaraException([], "Invalid backoff policy");
}
}
}
@@ -0,0 +1,27 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
class EqualJitterBackoffPolicy extends BackoffPolicy {
private $period;
private $cap;
public function __construct(array $option) {
parent::__construct($option);
if (!isset($option['period'])) {
throw new InvalidArgumentException("Period must be specified.");
}
$this->period = $option['period'];
// Default: 3 days
$this->cap = isset($option['cap']) ? $option['cap'] : 3 * 24 * 60 * 60 * 1000;
}
public function getDelayTime($ctx) {
$ceil = min(pow(2, $ctx->getRetryCount() * $this->period), $this->cap);
return $ceil / 2 + mt_rand(0, $ceil / 2);
}
}
@@ -0,0 +1,27 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
class ExponentialBackoffPolicy extends BackoffPolicy {
private $period;
private $cap;
public function __construct(array $option) {
parent::__construct($option);
if (!isset($option['period'])) {
throw new DaraException("Period must be specified.");
}
$this->period = $option['period'];
// Default: 3 days
$this->cap = isset($option['cap']) ? $option['cap'] : 3 * 24 * 60 * 60 * 1000;
}
public function getDelayTime($ctx) {
$randomTime = pow(2, $ctx->getRetryCount() * $this->period);
return ($randomTime > $this->cap) ? $this->cap : $randomTime;
}
}
@@ -0,0 +1,24 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
class FixedBackoffPolicy extends BackoffPolicy {
private $period;
public function __construct(array $option) {
parent::__construct($option);
if (!isset($option['period'])) {
throw new DaraException([], "Period must be specified.");
}
$this->period = $option['period'];
}
public function getDelayTime($ctx) {
return $this->period;
}
}
@@ -0,0 +1,27 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
class FullJitterBackoffPolicy extends BackoffPolicy {
private $period;
private $cap;
public function __construct(array $option) {
parent::__construct($option);
if (!isset($option['period'])) {
throw new DaraException("Period must be specified.");
}
$this->period = $option['period'];
// Default: 3 days
$this->cap = isset($option['cap']) ? $option['cap'] : 3 * 24 * 60 * 60 * 1000;
}
public function getDelayTime($ctx) {
$ceil = min(pow(2, $ctx->getRetryCount() * $this->period), $this->cap);
return mt_rand(0, $ceil);
}
}
@@ -0,0 +1,26 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
class RandomBackoffPolicy extends BackoffPolicy {
private $period;
private $cap;
public function __construct(array $option) {
parent::__construct($option);
if (!isset($option['period'])) {
throw new DaraException([], "Period must be specified.");
}
$this->period = $option['period'];
$this->cap = isset($option['cap']) ? $option['cap'] : 20000;
}
public function getDelayTime($ctx) {
$randomTime = mt_rand(0, $ctx->getRetryCount() * $this->period);
return ($randomTime > $this->cap) ? $this->cap : $randomTime;
}
}
@@ -0,0 +1,62 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
use AlibabaCloud\Dara\Exception\DaraException;
class RetryCondition {
private $maxAttempts;
private $backoff = null;
private $exception = [];
private $errorCode = [];
private $maxDelay;
public function __construct($condition) {
if(isset($condition['maxAttempts'])) {
$this->maxAttempts = $condition['maxAttempts'];
}
$this->backoff = isset($condition['backoff']) ? $condition['backoff'] : null;
$this->exception = isset($condition['exception']) ? $condition['exception'] : [];
$this->errorCode = isset($condition['errorCode']) ? $condition['errorCode'] : [];
$this->maxDelay = isset($condition['maxDelay']) ? $condition['maxDelay'] : [];
}
/**
* @return int
*/
public function getMaxAttempts() {
return $this->maxAttempts;
}
/**
* @return BackoffPolicy
*/
public function getBackoff() {
return $this->backoff;
}
/**
* @return string[]
*/
public function getException() {
return $this->exception;
}
/**
* @return string[]
*/
public function getErrorCode() {
return $this->errorCode;
}
/**
* @return int
*/
public function getMaxDelay() {
return $this->maxDelay;
}
}
@@ -0,0 +1,50 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
use AlibabaCloud\Dara\RetryPolicy\RetryCondition;
class RetryOptions {
private $retryable;
private $retryCondition;
private $noRetryCondition;
public function __construct($options) {
$this->retryable = $options['retryable'];
$this->retryCondition = array_map(function ($condition) {
if($condition instanceof RetryCondition) {
return $condition;
}
return new RetryCondition($condition);
}, isset($options['retryCondition']) ? $options['retryCondition'] : []);
$this->noRetryCondition = array_map(function ($condition) {
if($condition instanceof RetryCondition) {
return $condition;
}
return new RetryCondition($condition);
}, isset($options['noRetryCondition']) ? $options['noRetryCondition'] : []);
}
/**
* @return bool
*/
public function getRetryable() {
return $this->retryable;
}
/**
* @return RetryCondition[]
*/
public function getRetryCondition() {
return $this->retryCondition;
}
/**
* @return RetryCondition[]
*/
public function getNoRetryCondition() {
return $this->noRetryCondition;
}
}
@@ -0,0 +1,64 @@
<?php
namespace AlibabaCloud\Dara\RetryPolicy;
use AlibabaCloud\Dara\RetryPolicy\BackoffPolicy;
use AlibabaCloud\Dara\Request;
use AlibabaCloud\Dara\Response;
use AlibabaCloud\Dara\Exception\DaraException;
class RetryPolicyContext {
private $key;
private $retriesAttempted;
private $httpRequest;
private $httpResponse;
private $exception;
public function __construct($options) {
$this->key = isset($options['key']) ? $options['key'] : '';
$this->retriesAttempted = isset($options['retriesAttempted']) ? $options['retriesAttempted'] : 0;
$this->httpRequest = isset($options['httpRequest']) ? $options['httpRequest'] : null;
$this->httpResponse = isset($options['httpResponse']) ? $options['httpResponse'] : null;
$this->exception = isset($options['exception']) ? $options['exception'] : null;
}
/**
*
* @return int
*/
public function getRetryCount(){
return $this->retriesAttempted;
}
/**
*
* @return string
*/
public function getKey() {
return $this->key;
}
/**
*
* @return Request
*/
public function getHttpRequest() {
return $this->httpRequest;
}
/**
*
* @return Response
*/
public function getHttpResponse() {
return $this->httpResponse;
}
/**
*
* @return DaraException
*/
public function getException() {
return $this->exception;
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
namespace AlibabaCloud\Dara;
class Url
{
private $url = '';
private $path = '';
private $pathname = '';
private $protocol = '';
private $hostname = '';
private $host = '';
private $port = '';
private $hash = '';
private $search = '';
private $auth = '';
public function __construct($str) {
$this->url = $str;
}
public function path() {
if(empty($this->path)) {
return ;
}
$pathname = $this->pathname();
$query = $this->search();
$this->path = $pathname . '?' . $query;
return $this->path;
}
public function pathname() {
if(empty($this->pathname)) {
return $this->pathname;
}
$this->pathname = parse_url($ref, PHP_URL_PATH);
return $this->pathname;
}
public function protocol() {
if(empty($this->protocol)) {
return $this->protocol;
}
$this->protocol = parse_url($ref, PHP_URL_SCHEME);
return $this->protocol;
}
public function hostname() {
if(empty($this->hostname)) {
return $this->hostname;
}
$this->hostname = parse_url($ref, PHP_URL_HOST);
return $this->hostname;
}
public function host() {
if(empty($this->host)) {
return ;
}
$hostname = $this->hostname();
$port = $this->port();
$this->host = $hostname . $port;
return $this->host;
}
public function port() {
if(empty($this->port)) {
return $this->port;
}
$this->port = parse_url($ref, PHP_URL_PORT);
return $this->port;
}
public function hash() {
if(empty($this->hash)) {
return $this->hash;
}
$this->hash = parse_url($ref, PHP_URL_FRAGMENT);
return $this->hash;
}
public function search() {
if(empty($this->search)) {
return $this->search;
}
$this->search = parse_url($ref, PHP_URL_QUERY);
return $this->search;
}
public function href() {
return $this->href;
}
public function auth() {
if(empty($this->auth)) {
return $this->auth;
}
$username = parse_url($ref, PHP_URL_USER);
$password = parse_url($ref, PHP_URL_PASS);
$this->auth = $username . ':' . $password;
return $this->auth;
}
public static function parse($url) {
return new self($url);
}
public static function urlEncode($url) {
if (empty($raw)) {
throw new \InvalidArgumentException('not a valid value for parameter');
}
$str = urlencode($raw);
$str = str_replace("%20", "+", $str);
$str = str_replace("%2A", "*", $str);
return $str;
}
public static function percentEncode($raw) {
if($raw === null) {
return null;
}
$encoded = urlencode($raw);
$encoded = str_replace('+', '%20', $encoded);
$encoded = str_replace('*', '%2A', $encoded);
$encoded = str_replace('%7E', '~', $encoded);
return $encoded;
}
public static function pathEncode($path) {
if (empty($raw) || $raw === '/') {
return $raw;
}
$arr = explode('/', $raw);
$ret = '';
foreach ($arr as $i => $path) {
$str = self::percentEncode($path);
$ret .= "$str/";
}
return substr($ret, 0, -1);
}
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace AlibabaCloud\Dara\Util;
use XmlWriter;
/**
* Based on: http://stackoverflow.com/questions/99350/passing-php-associative-arrays-to-and-from-xml.
*/
class ArrayToXml
{
private $version;
private $encoding;
/**
* Construct ArrayToXML object with selected version and encoding
* for available values check XmlWriter docs http://www.php.net/manual/en/function.xmlwriter-start-document.php.
*
* @param string $xmlVersion XML Version, default 1.0
* @param string $xmlEncoding XML Encoding, default UTF-8
*/
public function __construct($xmlVersion = '1.0', $xmlEncoding = 'utf-8')
{
$this->version = $xmlVersion;
$this->encoding = $xmlEncoding;
}
/**
* Build an XML Data Set.
*
* @param array $data Associative Array containing values to be parsed into an XML Data Set(s)
* @param string $startElement Root Opening Tag, default data
*
* @return string XML String containing values
* @return mixed Boolean false on failure, string XML result on success
*/
public function buildXML($data, $startElement = 'data')
{
if (!\is_array($data)) {
$err = 'Invalid variable type supplied, expected array not found on line ' . __LINE__ . ' in Class: ' . __CLASS__ . ' Method: ' . __METHOD__;
trigger_error($err);
return false; //return false error occurred
}
$xml = new XmlWriter();
$xml->openMemory();
$xml->startDocument($this->version, $this->encoding);
$xml->startElement($startElement);
$data = $this->writeAttr($xml, $data);
$this->writeEl($xml, $data);
$xml->endElement(); //write end element
//returns the XML results
return $xml->outputMemory(true);
}
/**
* Write keys in $data prefixed with @ as XML attributes, if $data is an array.
* When an @ prefixed key is found, a '%' key is expected to indicate the element itself,
* and '#' prefixed key indicates CDATA content.
*
* @param XMLWriter $xml object
* @param array $data with attributes filtered out
*
* @return array $data | $nonAttributes
*/
protected function writeAttr(XMLWriter $xml, $data)
{
if (\is_array($data)) {
$nonAttributes = [];
foreach ($data as $key => $val) {
//handle an attribute with elements
if ('@' == $key[0]) {
$xml->writeAttribute(substr($key, 1), $val);
} elseif ('%' == $key[0]) {
if (\is_array($val)) {
$nonAttributes = $val;
} else {
$xml->text($val);
}
} elseif ('#' == $key[0]) {
if (\is_array($val)) {
$nonAttributes = $val;
} else {
$xml->startElement(substr($key, 1));
$xml->writeCData($val);
$xml->endElement();
}
} elseif ('!' == $key[0]) {
if (\is_array($val)) {
$nonAttributes = $val;
} else {
$xml->writeCData($val);
}
} //ignore normal elements
else {
$nonAttributes[$key] = $val;
}
}
return $nonAttributes;
}
return $data;
}
/**
* Write XML as per Associative Array.
*
* @param XMLWriter $xml object
* @param array $data Associative Data Array
*/
protected function writeEl(XMLWriter $xml, $data)
{
foreach ($data as $key => $value) {
if (\is_array($value) && !$this->isAssoc($value)) { //numeric array
foreach ($value as $itemValue) {
if (\is_array($itemValue)) {
$xml->startElement($key);
$itemValue = $this->writeAttr($xml, $itemValue);
$this->writeEl($xml, $itemValue);
$xml->endElement();
} else {
$itemValue = $this->writeAttr($xml, $itemValue);
$xml->writeElement($key, "{$itemValue}");
}
}
} elseif (\is_array($value)) { //associative array
$xml->startElement($key);
$value = $this->writeAttr($xml, $value);
$this->writeEl($xml, $value);
$xml->endElement();
} else { //scalar
$value = $this->writeAttr($xml, $value);
$xml->writeElement($key, "{$value}");
}
}
}
/**
* Check if array is associative with string based keys
* FROM: http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential/4254008#4254008.
*
* @param array $array Array to check
*
* @return bool
*/
protected function isAssoc($array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace AlibabaCloud\Dara\Util;
use AlibabaCloud\Dara\Exception\DaraException;
class BytesUtil
{
private static function is_bytes($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;
}
public static function from($input, $encoding = 'utf-8')
{
$buffer = '';
if (self::is_bytes($input)) {
return $input;
} elseif (is_string($input)) {
switch (strtolower($encoding)) {
case 'utf-8':
case 'utf8':
$buffer = $input;
break;
case 'base64':
$decoded = base64_decode($input);
if ($decoded === false) {
throw new DaraException([], 'Invalid base64 input.');
}
$buffer = $decoded;
break;
case 'hex':
$decoded = hex2bin($input);
if ($decoded === false) {
throw new DaraException([], 'Invalid hex input.');
}
$buffer = $decoded;
break;
default:
throw new DaraException([], 'Unsupported encoding type.');
}
} else {
throw new DaraException([], 'Input must be an bytes or a string.');
}
$result = [];
for ($i = 0, $len = strlen($buffer); $i < $len; $i++) {
$result[] = ord($buffer[$i]);
}
return $result;
}
/**
*
* @param int[] $bytes
* @return string
*/
public static function toString($bytes, $type = 'utf8')
{
if (\is_string($bytes)) {
return $bytes;
}
$str = '';
foreach ($bytes as $ch) {
$str .= \chr($ch);
}
if($type == 'hex') {
return bin2hex($str);
}
if($type == 'base64') {
return base64_encode($str);
}
return $str;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace AlibabaCloud\Dara\Util;
/**
* This is a console module.
*/
class Console
{
private static $stderrStream = null;
private static $stdoutStream = null;
/**
* Set custom stderr stream for testing
*/
public static function setStderrStream($stream)
{
self::$stderrStream = $stream;
}
/**
* Set custom stdout stream for testing
*/
public static function setStdoutStream($stream)
{
self::$stdoutStream = $stream;
}
/**
* Reset streams to default
*/
public static function resetStreams()
{
self::$stderrStream = null;
self::$stdoutStream = null;
}
/**
* Console val with log level into stdout.
*
* @param string $val the printing string
*/
public static function log($val)
{
self::output($val, 'LOG');
}
/**
* Console val with info level into stdout.
*
* @param string $val the printing string
*/
public static function info($val)
{
self::output($val, 'INFO');
}
/**
* Console val with warning level into stdout.
*
* @param string $val the printing string
*/
public static function warning($val)
{
self::output($val, 'WARNING');
}
/**
* Console val with debug level into stdout.
*
* @param string $val the printing string
*/
public static function debug($val)
{
self::output($val, 'DEBUG');
}
/**
* Console val with error level into stderr.
*
* @param string $val the printing string
*/
public static function error($val)
{
self::output($val, 'ERROR', true);
}
/**
* Outputs the message to the appropriate output stream.
*
* @param string $val The message to log
* @param string $level The log level label
* @param bool $stderr Whether to output to stderr
*/
private static function output($val, $level, $stderr = false)
{
$prefix = sprintf("[%s] ", $level);
$message = $prefix . $val . PHP_EOL;
if ($stderr) {
// Use custom stderr stream or default stderr
if (self::$stderrStream !== null) {
fwrite(self::$stderrStream, $message);
} else {
file_put_contents('php://stderr', $message);
}
} else {
// Use custom stdout stream or default echo
if (self::$stdoutStream !== null) {
fwrite(self::$stdoutStream, $message);
} else {
echo $message;
}
}
}
}
@@ -0,0 +1,41 @@
<?php
namespace AlibabaCloud\Dara\Util;
use Psr\Http\Message\StreamInterface;
// Load implementation based on PHP version
if (PHP_VERSION_ID >= 70100) {
// PHP 7.1+ supports return type declarations and nullable types
require_once __DIR__ . '/FileFormStreamTyped.php';
if (!class_exists('AlibabaCloud\\Dara\\Util\\FileFormStream', false)) {
class_alias('AlibabaCloud\\Dara\\Util\\FileFormStreamTyped', 'AlibabaCloud\\Dara\\Util\\FileFormStream');
}
} else {
// PHP 5.6-7.0 does not support void and nullable type declarations
/**
* @internal
* @coversNothing
*/
class FileFormStream implements StreamInterface
{
use FileFormStreamTrait;
public function __toString() { return $this->__toStringImpl(); }
public function getContents() { return $this->getContentsImpl(); }
public function close() { $this->closeImpl(); }
public function detach() { return $this->detachImpl(); }
public function getSize() { return $this->getSizeImpl(); }
public function tell() { return $this->tellImpl(); }
public function eof() { return $this->eofImpl(); }
public function isSeekable() { return $this->isSeekableImpl(); }
public function seek($offset, $whence = SEEK_SET) { $this->seekImpl($offset, $whence); }
public function rewind() { $this->rewindImpl(); }
public function isWritable() { return $this->isWritableImpl(); }
public function write($string) { return $this->writeImpl($string); }
public function isReadable() { return $this->isReadableImpl(); }
public function read($length) { return $this->readImpl($length); }
public function getMetadata($key = null) { return $this->getMetadataImpl($key); }
}
}
@@ -0,0 +1,284 @@
<?php
namespace AlibabaCloud\Dara\Util;
use AlibabaCloud\Dara\Models\FileField;
use GuzzleHttp\Psr7\Stream;
/**
* Shared implementation for FileFormStream
* @internal
*/
trait FileFormStreamTrait
{
/**
* @var resource
*/
private $stream;
private $index = 0;
private $form = [];
private $boundary = '';
private $streaming = false;
private $keys = [];
/**
* @var Stream
*/
private $currStream;
private $size;
private $uri;
private $seekable;
private $readable = true;
private $writable = true;
public function __construct($map, $boundary)
{
$this->stream = fopen('php://memory', 'a+');
$this->form = $map;
$this->boundary = $boundary;
$this->keys = array_keys($map);
do {
$read = $this->readForm(1024);
} while (null !== $read);
$meta = stream_get_meta_data($this->stream);
$this->seekable = $meta['seekable'];
$this->uri = $this->getMetadata('uri');
$this->seek(0);
$this->seek(0);
}
/**
* Closes the stream when the destructed.
*/
public function __destruct()
{
$this->close();
}
/**
* @return string
*/
protected function __toStringImpl()
{
try {
$this->seek(0);
return (string) stream_get_contents($this->stream);
} catch (\Exception $e) {
return '';
}
}
/**
* @param int $length
* @return false|int|string
*/
public function readForm($length)
{
if ($this->streaming) {
if (null !== $this->currStream) {
$content = $this->currStream->read($length);
if (false !== $content && '' !== $content) {
fwrite($this->stream, $content);
return $content;
}
return $this->next("\r\n");
}
return $this->next();
}
$keysCount = \count($this->keys);
if ($this->index > $keysCount) {
return null;
}
if ($keysCount > 0) {
if ($this->index < $keysCount) {
$this->streaming = true;
$name = $this->keys[$this->index];
$field = $this->form[$name];
if (!empty($field) && $field instanceof FileField) {
if (!empty($field->content)) {
$this->currStream = $field->content;
$str = '--' . $this->boundary . "\r\n" .
'Content-Disposition: form-data; name="' . $name . '"; filename="' . $field->filename . "\"\r\n" .
'Content-Type: ' . $field->contentType . "\r\n\r\n";
$this->write($str);
return $str;
}
return $this->next();
}
$val = $field;
$str = '--' . $this->boundary . "\r\n" .
'Content-Disposition: form-data; name="' . $name . "\"\r\n\r\n" .
$val . "\r\n";
fwrite($this->stream, $str);
return $str;
}
if ($this->index == $keysCount) {
return $this->next('--' . $this->boundary . "--\r\n");
}
return null;
}
return null;
}
protected function getContentsImpl()
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
$contents = stream_get_contents($this->stream);
if (false === $contents) {
throw new \RuntimeException('Unable to read stream contents');
}
return $contents;
}
protected function closeImpl()
{
if (isset($this->stream)) {
if (\is_resource($this->stream)) {
fclose($this->stream);
}
$this->detach();
}
}
protected function detachImpl()
{
if (!isset($this->stream)) {
return null;
}
$result = $this->stream;
unset($this->stream);
$this->size = $this->uri = null;
return $result;
}
protected function getSizeImpl()
{
if (null !== $this->size) {
return $this->size;
}
if (!isset($this->stream)) {
return null;
}
if ($this->uri) {
clearstatcache(true, $this->uri);
}
$stats = fstat($this->stream);
if (isset($stats['size'])) {
$this->size = $stats['size'];
return $this->size;
}
return null;
}
protected function isReadableImpl()
{
return $this->readable;
}
protected function isWritableImpl()
{
return $this->writable;
}
protected function isSeekableImpl()
{
return $this->seekable;
}
protected function eofImpl()
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
return feof($this->stream);
}
protected function tellImpl()
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
$result = ftell($this->stream);
if (false === $result) {
throw new \RuntimeException('Unable to determine stream position');
}
return $result;
}
protected function rewindImpl()
{
$this->seek(0);
}
protected function seekImpl($offset, $whence = SEEK_SET)
{
$whence = (int) $whence;
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
if (!$this->seekable) {
throw new \RuntimeException('Stream is not seekable');
}
if (-1 === fseek($this->stream, $offset, $whence)) {
throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . var_export($whence, true));
}
}
protected function readImpl($length)
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
if (!$this->readable) {
throw new \RuntimeException('Cannot read from non-readable stream');
}
if ($length < 0) {
throw new \RuntimeException('Length parameter cannot be negative');
}
if (0 === $length) {
return '';
}
$string = fread($this->stream, $length);
if (false === $string) {
throw new \RuntimeException('Unable to read from stream');
}
return $string;
}
protected function writeImpl($string)
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
if (!$this->writable) {
throw new \RuntimeException('Cannot write to a non-writable stream');
}
$this->size = null;
$result = fwrite($this->stream, $string);
if (false === $result) {
throw new \RuntimeException('Unable to write to stream');
}
return $result;
}
protected function getMetadataImpl($key = null)
{
if (!isset($this->stream)) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return isset($meta[$key]) ? $meta[$key] : null;
}
private function next($endStr = '')
{
$this->streaming = false;
++$this->index;
$this->write($endStr);
$this->currStream = null;
return $endStr;
}
}
@@ -0,0 +1,31 @@
<?php
namespace AlibabaCloud\Dara\Util;
use Psr\Http\Message\StreamInterface;
/**
* PHP 7.1+ version with return type declarations for PSR-7 v2.0 compatibility
* @internal
* @coversNothing
*/
class FileFormStreamTyped implements StreamInterface
{
use FileFormStreamTrait;
public function __toString(): string { return $this->__toStringImpl(); }
public function getContents(): string { return $this->getContentsImpl(); }
public function close(): void { $this->closeImpl(); }
public function detach() { return $this->detachImpl(); }
public function getSize(): ?int { return $this->getSizeImpl(); }
public function tell(): int { return $this->tellImpl(); }
public function eof(): bool { return $this->eofImpl(); }
public function isSeekable(): bool { return $this->isSeekableImpl(); }
public function seek($offset, $whence = SEEK_SET): void { $this->seekImpl($offset, $whence); }
public function rewind(): void { $this->rewindImpl(); }
public function isWritable(): bool { return $this->isWritableImpl(); }
public function write($string): int { return $this->writeImpl($string); }
public function isReadable(): bool { return $this->isReadableImpl(); }
public function read($length): string { return $this->readImpl($length); }
public function getMetadata($key = null) { return $this->getMetadataImpl($key); }
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace AlibabaCloud\Dara\Util;
use AlibabaCloud\Dara\Model;
class FormUtil
{
/**
*
* @param array $query
* @return string
*/
public static function toFormString($query)
{
if (null === $query) {
return '';
}
if ($query instanceof Model) {
$query = $query->toArray();
}
return str_replace('+', '%20', http_build_query($query));
}
/**
*
* @return string
*/
public static function getBoundary()
{
return (string) (mt_rand(10000000000000, 99999999999999));
}
/**
*
* @param array $map
* @param string $boundary
* @return string
*/
public static function toFileForm($map, $boundary)
{
return new FileFormStream($map, $boundary);
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace AlibabaCloud\Dara\Util;
class MathUtil
{
/**
*
* @return double
*/
public static function random()
{
return rand(0, getrandmax() - 1) / getrandmax();
}
}
+182
View File
@@ -0,0 +1,182 @@
<?php
namespace AlibabaCloud\Dara\Util;
use GuzzleHttp\Psr7\Stream;
use AlibabaCloud\Dara\Util\StringUtil;
use AlibabaCloud\Dara\Models\SSEEvent;
class StreamUtil
{
/**
* @param Stream $stream
*
* @return int[]
*/
public static function readAsBytes($stream)
{
$str = self::readAsString($stream);
return StringUtil::toBytes($str);
}
/**
* @param Stream $stream
*
* @return array the parsed result
*/
public static function readAsJSON($stream)
{
$jsonString = self::readAsString($stream);
return json_decode($jsonString, true);
}
/**
* @param Stream $stream
*
* @return string
*/
public static function readAsString($stream)
{
if ($stream->isSeekable()) {
$stream->rewind();
}
return $stream->getContents();
}
private static function tryGetEvents($head, $chunk) {
$all = $head . $chunk;
if(empty($all)) {
return [];
}
$start = 0;
$events = [];
$lines = explode("\n", $all);
$event = new SSEEvent();
for ($i = 0; $i < strlen($all) - 1; $i++) {
$c = $all[$i];
$c2 = $all[$i + 1];
if ($c === "\n" && $c2 === "\n") {
$part = substr($all, $start, $i - $start);
$lines = explode("\n", $part);
$event = new SSEEvent();
foreach ($lines as $line) {
if ('' === trim($line)) {
continue;
} elseif (0 === strpos($line, 'data:')) {
$data = substr($line, 5);
$event->data .= trim($data);
} elseif (0 === strpos($line, 'event:')) {
$eventLine = substr($line, 6);
$event->event = trim($eventLine);
} elseif (0 === strpos($line, 'id:')) {
$id = substr($line, 3);
$event->id = trim($id);
} elseif (0 === strpos($line, 'retry:')) {
$retry = substr($line, 6);
$retry = trim($retry);
if (ctype_digit($retry)) {
$event->retry = intval($retry, 10);
}
} elseif (isset($line[0]) && $line[0] === ':') {
// Lines starting with ':' are comments and ignored.
}
}
array_push($events, $event);
$start = $i + 2;
}
}
$remain = substr($all, $start);
return ['events' => $events, 'remain' => $remain];
}
/**
* @param Stream $stream
*
* @return string
*/
public static function readAsSSE($stream)
{
$rest = '';
while (!$stream->eof()) {
$chunk = $stream->read(4096);
$result = self::tryGetEvents($rest, $chunk);
if(empty($result)) {
continue;
}
$events = $result['events'];
$rest = $result['remain'];
foreach ($events as $event) {
yield $event;
}
}
// If there is any remaining data that qualifies as an event, yield it as well
if ($rest !== '' && $rest !== false) {
$lastEvent = new SSEEvent();
$lastEvent->data = $rest;
yield $lastEvent;
}
}
/**
* Create and return a Guzzle Stream from various inputs.
*
* @param mixed $str string|resource|callable|\GuzzleHttp\Psr7\Stream|object
* @return Stream
* @throws \InvalidArgumentException|\RuntimeException
*/
public static function streamFor($str)
{
if (!class_exists('\GuzzleHttp\Psr7\Stream')) {
throw new \RuntimeException('guzzlehttp/psr7 is required for streamFor');
}
// Already a Guzzle Stream
if ($str instanceof Stream) {
return $str;
}
// If callable, call and recurse
if (is_callable($str)) {
return self::streamFor($str());
}
// If resource (stream), wrap directly
if (is_resource($str)) {
if (get_resource_type($str) !== 'stream') {
throw new \InvalidArgumentException('Provided resource is not a stream');
}
return new Stream($str);
}
// If object with __toString, cast it
if (is_object($str) && method_exists($str, '__toString')) {
$str = (string)$str;
}
// Must be string (or scalar convertible)
if (!is_string($str) && !is_scalar($str) && !is_null($str)) {
throw new \InvalidArgumentException('Unsupported type for streamFor: ' . gettype($str));
}
$content = $str === null ? '' : (string)$str;
$resource = @fopen('php://temp', 'w+b');
if ($resource === false) {
throw new \RuntimeException('Failed to open temporary stream');
}
if ($content !== '') {
fwrite($resource, $content);
rewind($resource);
}
return new Stream($resource);
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace AlibabaCloud\Dara\Util;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\Util\BytesUtil;
class StringUtil
{
/**
* @param string $string
* @param string $type
*
* @return int[]
*/
public static function toBytes($string, $type = 'utf8')
{
return BytesUtil::from($string, $type);
}
/**
* @param string $str
* @param string $prefix
*
* @return bool
*/
public static function hasPrefix($str, $prefix)
{
if(!is_string($prefix) || !is_string($str)) {
return false;
}
$length = strlen($prefix);
if ($length == 0) {
return true;
}
return substr($str, 0, $length) === $prefix;
}
/**
* @param string $str
* @param string $suffix
*
* @return bool
*/
public static function hasSuffix($str, $suffix)
{
if(!is_string($suffix) || !is_string($str)) {
return false;
}
$length = strlen($suffix);
if ($length == 0) {
return true;
}
return substr($str, -$length) === $suffix;
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace AlibabaCloud\Dara\Util;
use AlibabaCloud\Dara\Util\ArrayToXml;
class XML
{
public static function parseXml($xmlStr, $response)
{
$res = self::parse($xmlStr);
if ($response === null) {
return $res;
} else {
if (\is_string($response)) {
$response = new $response();
}
$prop = get_object_vars($response);
$target = [];
foreach ($res as $k => $v) {
if (isset($prop[$k])) {
$target[$k] = $v;
}
}
return $target;
}
}
public static function toXML($array)
{
$arrayToXml = new ArrayToXml();
if (\is_object($array)) {
$tmp = explode('\\', \get_class($array));
$rootName = $tmp[\count($tmp) - 1];
$data = json_decode(json_encode($array), true);
} else {
$tmp = $array;
reset($tmp);
$rootName = key($tmp);
$data = $array[$rootName];
}
ksort($data);
return $arrayToXml->buildXML($data, $rootName);
}
private static function parse($xml)
{
if (\PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader(true);
}
return json_decode(
json_encode(
simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)
),
true
);
}
}
@@ -0,0 +1,15 @@
composer.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
@@ -0,0 +1,66 @@
<?php
/*
* This document has been generated with
* https://mlocati.github.io/php-cs-fixer-configurator/#version:2.15|configurator
* you can change this configuration by importing this file.
*/
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
return (new Config())
->setRiskyAllowed(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,
'phpdoc_separation' => true,
'single_quote' => true,
'declare_equal_normalize' => true,
'function_typehint_space' => true,
'include' => true,
'lowercase_cast' => 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,
'blank_lines_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' => true,
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true,
'no_extra_blank_lines' => [
'tokens' => [
'curly_brace_block',
'extra',
'parenthesis_brace_block',
'square_brace_block',
'throw',
'use',
]
],
'braces' => [
'allow_single_line_closure' => true,
],
])
->setFinder(
(new Finder())
->exclude('vendor')
->exclude('tests')
->in(__DIR__)
)
;
+222
View File
@@ -0,0 +1,222 @@
2026-07-01 Version: 4.6.0
- Support API AddRcsSignMenu.
- Support API CreateRCSMobileCapableTask.
- Support API CreateRCSTemplate.
- Support API GetRCSSignature.
- Support API QueryRCSMobileCapable.
- Support API QueryRCSMobileCapableTaskResult.
- Support API QueryRCSTemplate.
- Support API QueryRcsSignMenuByVersion.
- Support API SendRCS.
- Support API SendRCSReply.
- Support API UpdateRCSSignature.
- Support API UpgradeToRCSSignature.
- Update API CreateSmsAppIcpRecord: add request parameters AppRuntimePic.
- Update API CreateSmsAppIcpRecord: add request parameters AppStoreDownloadPic.
- Update API QuerySmsAppIcpRecord: add response parameters Body.Data.$.AppRuntimePic.
- Update API QuerySmsAppIcpRecord: add response parameters Body.Data.$.AppRuntimePicUrl.
- Update API QuerySmsAppIcpRecord: add response parameters Body.Data.$.AppStoreDownloadPic.
- Update API QuerySmsAppIcpRecord: add response parameters Body.Data.$.AppStoreDownloadPicUrl.
2026-04-07 Version: 4.5.1
- Update API GetSmsTemplate: add response parameters Body.SignList.
- Update API GetSmsTemplateList: add response parameters Body.Data.List.$.TemplateContent.
2026-02-10 Version: 4.5.0
- Support API CreateDigitalSignOrder.
- Support API CreateDigitalSmsTemplate.
- Support API QueryDigitalSignByName.
2026-01-05 Version: 4.4.0
- Support API CreateSmsAppIcpRecord.
- Support API CreateSmsTrademark.
- Support API GetSmsTemplateList.
- Support API QuerySmsAppIcpRecord.
- Support API QuerySmsTrademark.
2025-12-11 Version: 4.3.1
- Update API CreateSmsSign: add request parameters AppIcpRecordId.
- Update API CreateSmsSign: add request parameters TrademarkId.
- Update API GetSmsSign: add response parameters Body.AppIcpRecordId.
- Update API GetSmsSign: add response parameters Body.TrademarkId.
- Update API QuerySmsSignList: add response parameters Body.SmsSignList.$.AppIcpRecordId.
- Update API QuerySmsSignList: add response parameters Body.SmsSignList.$.TrademarkId.
- Update API UpdateSmsSign: add request parameters AppIcpRecordId.
- Update API UpdateSmsSign: add request parameters TrademarkId.
2025-11-27 Version: 4.3.0
- Support API GetSmsOcrOssInfo.
- Update API QuerySmsTemplateList: add response parameters Body.SmsTemplateList.$.TrafficDriving.
2025-09-17 Version: 4.2.0
- Support API SendLogisticsSms.
- Support API VerifyLogisticsSmsMailNo.
2025-09-11 Version: 4.1.3
- Update API CreateSmsTemplate: add request parameters TrafficDriving.
- Update API QuerySmsTemplateList: add response parameters Body.SmsTemplateList.$.SignatureName.
- Update API UpdateSmsTemplate: add request parameters TrafficDriving.
2025-09-11 Version: 4.1.3
- Update API CreateSmsTemplate: add request parameters TrafficDriving.
- Update API QuerySmsTemplateList: add response parameters Body.SmsTemplateList.$.SignatureName.
- Update API UpdateSmsTemplate: add request parameters TrafficDriving.
2025-06-30 Version: 4.1.2
- Update API GetSmsSign: add response parameters Body.SignIspRegisterDetailList.
2025-05-27 Version: 4.1.1
- Update API QueryMobilesCardSupport: add request parameters EncryptType.
2025-04-23 Version: 4.1.0
- Support API DeleteSmsQualification.
- Support API QuerySingleSmsQualification.
- Support API QuerySmsQualificationRecord.
- Support API RequiredPhoneCode.
- Support API SubmitSmsQualification.
- Support API UpdateSmsQualification.
- Support API ValidPhoneCode.
2025-04-22 Version: 4.0.0
- Support API ChangeSignatureQualification.
- Support API CreateSmsAuthorizationLetter.
- Support API GetQualificationOssInfo.
- Support API QuerySmsAuthorizationLetter.
- Update API CreateSmsSign: update request parameters AuthorizationLetterId' type has changed.
- Update API CreateSmsSign: update request parameters AuthorizationLetterId' format has changed.
- Update API GetSmsSign: update response parameters Body.AuthorizationLetterId' type has changed.
- Update API GetSmsSign: update response parameters Body.AuthorizationLetterId' format has changed.
- Update API QuerySmsSignList: update response parameters Body.SmsSignList.$.AuthorizationLetterId' type has changed.
- Update API QuerySmsSignList: update response parameters Body.SmsSignList.$.AuthorizationLetterId' format has changed.
- Update API UpdateSmsSign: update request parameters AuthorizationLetterId' type has changed.
- Update API UpdateSmsSign: update request parameters AuthorizationLetterId' format has changed.
2025-04-16 Version: 3.1.3
- Update API CreateSmsSign: add request parameters AuthorizationLetterId.
- Update API GetSmsSign: add response parameters Body.AuthorizationLetterAuditPass.
- Update API GetSmsSign: add response parameters Body.AuthorizationLetterId.
- Update API QuerySmsSignList: add response parameters Body.SmsSignList.$.AuthorizationLetterId.
- Update API QuerySmsSignList: add response parameters Body.SmsSignList.$.authorizationLetterAuditPass.
- Update API UpdateSmsSign: add request parameters AuthorizationLetterId.
2025-03-26 Version: 3.1.2
- Update API GetSmsTemplate: add response parameters Body.VendorAuditStatus.
2025-01-03 Version: 3.1.1
- Update API GetSmsSign: update response param.
- Update API QueryExtCodeSign: update param ExtCode.
- Update API QueryExtCodeSign: update param SignName.
2024-10-24 Version: 3.1.0
- Support API AddExtCodeSign.
- Support API DeleteExtCodeSign.
- Support API GetCardSmsDetails.
- Support API QueryExtCodeSign.
- Support API UpdateExtCodeSign.
2024-06-25 Version: 3.0.0
- Support API CreateSmsSign.
- Support API CreateSmsTemplate.
- Support API GetOSSInfoForUploadFile.
- Support API GetSmsSign.
- Support API GetSmsTemplate.
- Support API UpdateSmsSign.
- Support API UpdateSmsTemplate.
- Update API CreateSmartShortUrl: add param OutId.
- Update API CreateSmartShortUrl: delete param Expiration.
- Update API CreateSmartShortUrl: delete param SourceName.
- Update API CreateSmartShortUrl: update param PhoneNumbers.
- Update API CreateSmartShortUrl: update param SourceUrl.
- Update API QueryPageSmartShortUrlLog: delete param ClickState.
- Update API QueryPageSmartShortUrlLog: delete param EndId.
- Update API QueryPageSmartShortUrlLog: delete param ShortName.
- Update API QueryPageSmartShortUrlLog: delete param StartId.
- Update API QueryPageSmartShortUrlLog: update param CreateDateEnd.
- Update API QueryPageSmartShortUrlLog: update param CreateDateStart.
- Update API QueryPageSmartShortUrlLog: update param PageNo.
- Update API QueryPageSmartShortUrlLog: update param PageSize.
2023-07-04 Version: 2.0.24
- Add CreateSmartShortUrl api.
2022-11-29 Version: 2.0.23
- Add custom content for QueryCardSmsTemplateReport.
2022-10-11 Version: 2.0.22
- Add custom content for QueryCardSmsTemplateReport.
2022-09-30 Version: 2.0.21
- Add custom content for SendBatchSms.
2022-09-29 Version: 2.0.20
- Add outId for SendBatchSms.
2022-09-28 Version: 2.0.19
- Upgrade formdata for CheckMobilesCardTemplateSupport.
2022-08-11 Version: 2.0.18
- Upgrade formdata for SendBatchSms.
2022-08-03 Version: 2.0.17
- Upgrade Service for SmsStatistics.
2022-07-14 Version: 2.0.16
- Upgrade Service for SmsTemplate.
2022-07-06 Version: 2.0.15
- Upgrade Service for SmsSign.
2022-07-06 Version: 2.0.14
- Upgrade Service for SmsSign.
2022-07-04 Version: 2.0.13
- Upgrade Service for CardSms.
2022-06-29 Version: 2.0.12
- Upgrade Service for Template and Sign.
2022-06-17 Version: 2.0.10
- Upgrade Service for CARDSMS.
2022-01-24 Version: 2.0.9
- Generated php 2017-05-25 for Dysmsapi.
2021-11-29 Version: 2.0.8
- Upgrade Service for SMS.
2021-11-16 Version: 2.0.7
- Upgrade Service for SMS.
2021-10-26 Version: 2.0.6
- Support Short Url for SMS.
2021-09-01 Version: 1.0.3
- Generated php 2017-05-25 for Dysmsapi.
2021-07-15 Version: 1.0.2
- Generated php 2017-05-25 for Dysmsapi.
2021-01-04 Version: 1.0.1
- AMP Version Change.
2020-12-29 Version: 1.0.0
- AMP Version Change.
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Some files were not shown because too many files have changed in this diff Show More