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