mirror of
https://github.com/LDAPAccountManager/lam.git
synced 2025-10-03 01:39:33 +02:00
9.3
This commit is contained in:
parent
d78ddb43b1
commit
0593b55ed9
345 changed files with 3911 additions and 1907 deletions
430
lam/composer.lock
generated
430
lam/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -60,9 +60,7 @@ abstract class AbstractErrorParser
|
|||
foreach ($errors as $key => $error) {
|
||||
|
||||
// If error code matches a known error shape, populate the body
|
||||
if ($data['code'] == $error['name']
|
||||
&& $error instanceof StructureShape
|
||||
) {
|
||||
if ($this->errorCodeMatches($data, $error)) {
|
||||
$modeledError = $error;
|
||||
$data['body'] = $this->extractPayload(
|
||||
$modeledError,
|
||||
|
@ -92,4 +90,10 @@ abstract class AbstractErrorParser
|
|||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
private function errorCodeMatches(array $data, $error): bool
|
||||
{
|
||||
return $data['code'] == $error['name']
|
||||
|| (isset($error['error']['code']) && $data['code'] === $error['error']['code']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,41 +12,133 @@ trait JsonParserTrait
|
|||
{
|
||||
use PayloadParserTrait;
|
||||
|
||||
private function genericHandler(ResponseInterface $response)
|
||||
private function genericHandler(ResponseInterface $response): array
|
||||
{
|
||||
$code = (string) $response->getStatusCode();
|
||||
$error_code = null;
|
||||
$error_type = null;
|
||||
|
||||
// Parse error code and type for query compatible services
|
||||
if ($this->api
|
||||
&& !is_null($this->api->getMetadata('awsQueryCompatible'))
|
||||
&& $response->getHeaderLine('x-amzn-query-error')
|
||||
&& $response->hasHeader('x-amzn-query-error')
|
||||
) {
|
||||
$queryError = $response->getHeaderLine('x-amzn-query-error');
|
||||
$parts = explode(';', $queryError);
|
||||
if (isset($parts) && count($parts) == 2 && $parts[0] && $parts[1]) {
|
||||
$error_code = $parts[0];
|
||||
$error_type = $parts[1];
|
||||
$awsQueryError = $this->parseAwsQueryCompatibleHeader($response);
|
||||
if ($awsQueryError) {
|
||||
$error_code = $awsQueryError['code'];
|
||||
$error_type = $awsQueryError['type'];
|
||||
}
|
||||
}
|
||||
|
||||
// Parse error code from X-Amzn-Errortype header
|
||||
if (!$error_code && $response->hasHeader('X-Amzn-Errortype')) {
|
||||
$error_code = $this->extractErrorCode(
|
||||
$response->getHeaderLine('X-Amzn-Errortype')
|
||||
);
|
||||
}
|
||||
|
||||
$parsedBody = null;
|
||||
$body = $response->getBody();
|
||||
if (!$body->isSeekable() || $body->getSize()) {
|
||||
$parsedBody = $this->parseJson((string) $body, $response);
|
||||
}
|
||||
|
||||
// Parse error code from response body
|
||||
if (!$error_code && $parsedBody) {
|
||||
$error_code = $this->parseErrorFromBody($parsedBody);
|
||||
}
|
||||
|
||||
if (!isset($error_type)) {
|
||||
$error_type = $code[0] == '4' ? 'client' : 'server';
|
||||
}
|
||||
|
||||
return [
|
||||
'request_id' => (string) $response->getHeaderLine('x-amzn-requestid'),
|
||||
'code' => isset($error_code) ? $error_code : null,
|
||||
'request_id' => $response->getHeaderLine('x-amzn-requestid'),
|
||||
'code' => $error_code ?? null,
|
||||
'message' => null,
|
||||
'type' => $error_type,
|
||||
'parsed' => $this->parseJson($response->getBody(), $response)
|
||||
'parsed' => $parsedBody
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse AWS Query Compatible error from header
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @return array|null Returns ['code' => string, 'type' => string] or null
|
||||
*/
|
||||
private function parseAwsQueryCompatibleHeader(ResponseInterface $response): ?array
|
||||
{
|
||||
$queryError = $response->getHeaderLine('x-amzn-query-error');
|
||||
$parts = explode(';', $queryError);
|
||||
|
||||
if (count($parts) === 2 && $parts[0] && $parts[1]) {
|
||||
return [
|
||||
'code' => $parts[0],
|
||||
'type' => $parts[1]
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse error code from response body
|
||||
*
|
||||
* @param array|null $parsedBody
|
||||
* @return string|null
|
||||
*/
|
||||
private function parseErrorFromBody(?array $parsedBody): ?string
|
||||
{
|
||||
if (!$parsedBody
|
||||
|| (!isset($parsedBody['code']) && !isset($parsedBody['__type']))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$error_code = $parsedBody['code'] ?? $parsedBody['__type'];
|
||||
return $this->extractErrorCode($error_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract error code from raw error string containing # and/or : delimiters
|
||||
*
|
||||
* @param string $rawErrorCode
|
||||
* @return string
|
||||
*/
|
||||
private function extractErrorCode(string $rawErrorCode): string
|
||||
{
|
||||
// Handle format with both # and uri (e.g., "namespace#http://foo-bar")
|
||||
if (str_contains($rawErrorCode, ':') && str_contains($rawErrorCode, '#')) {
|
||||
$start = strpos($rawErrorCode, '#') + 1;
|
||||
$end = strpos($rawErrorCode, ':', $start);
|
||||
return substr($rawErrorCode, $start, $end - $start);
|
||||
}
|
||||
|
||||
// Handle format with uri only : (e.g., "ErrorCode:http://foo-bar.com/baz")
|
||||
if (str_contains($rawErrorCode, ':')) {
|
||||
return substr($rawErrorCode, 0, strpos($rawErrorCode, ':'));
|
||||
}
|
||||
|
||||
// Handle format with only # (e.g., "namespace#ErrorCode")
|
||||
if (str_contains($rawErrorCode, '#')) {
|
||||
return substr($rawErrorCode, strpos($rawErrorCode, '#') + 1);
|
||||
}
|
||||
|
||||
return $rawErrorCode;
|
||||
}
|
||||
|
||||
protected function payload(
|
||||
ResponseInterface $response,
|
||||
StructureShape $member
|
||||
) {
|
||||
$jsonBody = $this->parseJson($response->getBody(), $response);
|
||||
|
||||
if ($jsonBody) {
|
||||
return $this->parser->parse($member, $jsonBody);
|
||||
$body = $response->getBody();
|
||||
if (!$body->isSeekable() || $body->getSize()) {
|
||||
$jsonBody = $this->parseJson($body, $response);
|
||||
} else {
|
||||
$jsonBody = (string) $body;
|
||||
}
|
||||
|
||||
return $this->parser->parse($member, $jsonBody);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,7 @@ class JsonRpcErrorParser extends AbstractErrorParser
|
|||
$parts = explode('#', $data['parsed']['__type']);
|
||||
$data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
|
||||
}
|
||||
$data['message'] = isset($data['parsed']['message'])
|
||||
? $data['parsed']['message']
|
||||
: null;
|
||||
$data['message'] = $data['parsed']['message'] ?? null;
|
||||
}
|
||||
|
||||
$this->populateShape($data, $response, $command);
|
||||
|
|
|
@ -30,7 +30,7 @@ class RestJsonErrorParser extends AbstractErrorParser
|
|||
|
||||
// Merge in error data from the JSON body
|
||||
if ($json = $data['parsed']) {
|
||||
$data = array_replace($data, $json);
|
||||
$data = array_replace($json, $data);
|
||||
}
|
||||
|
||||
// Correct error type from services like Amazon Glacier
|
||||
|
@ -38,18 +38,9 @@ class RestJsonErrorParser extends AbstractErrorParser
|
|||
$data['type'] = strtolower($data['type']);
|
||||
}
|
||||
|
||||
// Retrieve the error code from services like Amazon Elastic Transcoder
|
||||
if ($code = $response->getHeaderLine('x-amzn-errortype')) {
|
||||
$colon = strpos($code, ':');
|
||||
$data['code'] = $colon ? substr($code, 0, $colon) : $code;
|
||||
}
|
||||
|
||||
// Retrieve error message directly
|
||||
$data['message'] = isset($data['parsed']['message'])
|
||||
? $data['parsed']['message']
|
||||
: (isset($data['parsed']['Message'])
|
||||
? $data['parsed']['Message']
|
||||
: null);
|
||||
$data['message'] = $data['parsed']['message']
|
||||
?? ($data['parsed']['Message'] ?? null);
|
||||
|
||||
$this->populateShape($data, $response, $command);
|
||||
|
||||
|
|
|
@ -55,8 +55,9 @@ abstract class AbstractRestParser extends AbstractParser
|
|||
}
|
||||
}
|
||||
|
||||
$body = $response->getBody();
|
||||
if (!$payload
|
||||
&& $response->getBody()->getSize() > 0
|
||||
&& (!$body->isSeekable() || $body->getSize())
|
||||
&& count($output->getMembers()) > 0
|
||||
) {
|
||||
// if no payload was found, then parse the contents of the body
|
||||
|
@ -73,20 +74,26 @@ abstract class AbstractRestParser extends AbstractParser
|
|||
array &$result
|
||||
) {
|
||||
$member = $output->getMember($payload);
|
||||
$body = $response->getBody();
|
||||
|
||||
if (!empty($member['eventstream'])) {
|
||||
$result[$payload] = new EventParsingIterator(
|
||||
$response->getBody(),
|
||||
$body,
|
||||
$member,
|
||||
$this
|
||||
);
|
||||
} else if ($member instanceof StructureShape) {
|
||||
// Structure members parse top-level data into a specific key.
|
||||
} elseif ($member instanceof StructureShape) {
|
||||
//Unions must have at least one member set to a non-null value
|
||||
// If the body is empty, we can assume it is unset
|
||||
if (!empty($member['union']) && ($body->isSeekable() && !$body->getSize())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result[$payload] = [];
|
||||
$this->payload($response, $member, $result[$payload]);
|
||||
} else {
|
||||
// Streaming data is just the stream from the response body.
|
||||
$result[$payload] = $response->getBody();
|
||||
// Always set the payload to the body stream, regardless of content
|
||||
$result[$payload] = $body;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,13 +107,21 @@ abstract class AbstractRestParser extends AbstractParser
|
|||
&$result
|
||||
) {
|
||||
$value = $response->getHeaderLine($shape['locationName'] ?: $name);
|
||||
// Empty headers should not be deserialized
|
||||
if ($value === null || $value === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($shape->getType()) {
|
||||
case 'float':
|
||||
case 'double':
|
||||
$value = (float) $value;
|
||||
$value = match ($value) {
|
||||
'NaN', 'Infinity', '-Infinity' => $value,
|
||||
default => (float) $value
|
||||
};
|
||||
break;
|
||||
case 'long':
|
||||
case 'integer':
|
||||
$value = (int) $value;
|
||||
break;
|
||||
case 'boolean':
|
||||
|
@ -143,6 +158,23 @@ abstract class AbstractRestParser extends AbstractParser
|
|||
//output structure.
|
||||
return;
|
||||
}
|
||||
case 'list':
|
||||
$listMember = $shape->getMember();
|
||||
$type = $listMember->getType();
|
||||
|
||||
// Only boolean lists require special handling
|
||||
// other types can be returned as-is
|
||||
if ($type !== 'boolean') {
|
||||
break;
|
||||
}
|
||||
|
||||
$items = array_map('trim', explode(',', $value));
|
||||
$value = array_map(
|
||||
static fn($item) => filter_var($item, FILTER_VALIDATE_BOOLEAN),
|
||||
$items
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$result[$name] = $value;
|
||||
|
|
|
@ -50,7 +50,10 @@ class JsonParser
|
|||
$values = $shape->getValue();
|
||||
$target = [];
|
||||
foreach ($value as $k => $v) {
|
||||
$target[$k] = $this->parse($values, $v);
|
||||
// null map values should not be deserialized
|
||||
if (!is_null($v)) {
|
||||
$target[$k] = $this->parse($values, $v);
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ trait MetadataParserTrait
|
|||
&$result
|
||||
) {
|
||||
$value = $response->getHeaderLine($shape['locationName'] ?: $name);
|
||||
// Empty values should not be deserialized
|
||||
if ($value === null || $value === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($shape->getType()) {
|
||||
case 'float':
|
||||
|
@ -24,6 +28,7 @@ trait MetadataParserTrait
|
|||
$value = (float) $value;
|
||||
break;
|
||||
case 'long':
|
||||
case 'integer':
|
||||
$value = (int) $value;
|
||||
break;
|
||||
case 'boolean':
|
||||
|
|
|
@ -40,7 +40,15 @@ class QueryParser extends AbstractParser
|
|||
ResponseInterface $response
|
||||
) {
|
||||
$output = $this->api->getOperation($command->getName())->getOutput();
|
||||
$xml = $this->parseXml($response->getBody(), $response);
|
||||
$body = $response->getBody();
|
||||
$xml = !$body->isSeekable() || $body->getSize()
|
||||
? $this->parseXml($body, $response)
|
||||
: null;
|
||||
|
||||
// Empty request bodies should not be deserialized.
|
||||
if (is_null($xml)) {
|
||||
return new Result();
|
||||
}
|
||||
|
||||
if ($this->honorResultWrapper && $output['resultWrapper']) {
|
||||
$xml = $xml->{$output['resultWrapper']};
|
||||
|
|
|
@ -28,10 +28,25 @@ class RestJsonParser extends AbstractRestParser
|
|||
StructureShape $member,
|
||||
array &$result
|
||||
) {
|
||||
$jsonBody = $this->parseJson($response->getBody(), $response);
|
||||
$responseBody = (string) $response->getBody();
|
||||
|
||||
if ($jsonBody) {
|
||||
$result += $this->parser->parse($member, $jsonBody);
|
||||
// Parse JSON if we have content
|
||||
$parsedJson = null;
|
||||
if (!empty($responseBody)) {
|
||||
$parsedJson = $this->parseJson($responseBody, $response);
|
||||
} else {
|
||||
// An empty response body should be deserialized as null
|
||||
$result = $parsedJson;
|
||||
return;
|
||||
}
|
||||
|
||||
$parsedBody = $this->parser->parse($member, $parsedJson);
|
||||
if (is_string($parsedBody) && $member['document']) {
|
||||
// Document types can be strings: replace entire result
|
||||
$result = $parsedBody;
|
||||
} else {
|
||||
// Merge array/object results into existing result
|
||||
$result = array_merge($result, (array) $parsedBody);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,15 +76,18 @@ class XmlParser
|
|||
|
||||
private function memberKey(Shape $shape, $name)
|
||||
{
|
||||
if (null !== $shape['locationName']) {
|
||||
return $shape['locationName'];
|
||||
// Check if locationName came from shape definition
|
||||
if ($shape instanceof StructureShape && isset($shape['locationName'])) {
|
||||
$originalDef = $shape->getOriginalDefinition($shape->getName());
|
||||
|
||||
if ($originalDef && isset($originalDef['locationName'])
|
||||
&& $originalDef['locationName'] === $shape['locationName']
|
||||
) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
if ($shape instanceof ListShape && $shape['flattened']) {
|
||||
return $shape->getMember()['locationName'] ?: $name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
return $shape['locationName'] ?? $name;
|
||||
}
|
||||
|
||||
private function parse_list(ListShape $shape, \SimpleXMLElement $value)
|
||||
|
@ -132,7 +135,12 @@ class XmlParser
|
|||
|
||||
private function parse_float(Shape $shape, $value)
|
||||
{
|
||||
return (float) (string) $value;
|
||||
$value = (string) $value;
|
||||
|
||||
return match ($value) {
|
||||
'NaN', 'Infinity', '-Infinity' => $value,
|
||||
default => (float) $value
|
||||
};
|
||||
}
|
||||
|
||||
private function parse_integer(Shape $shape, $value)
|
||||
|
@ -162,12 +170,8 @@ class XmlParser
|
|||
|
||||
private function parse_xml_attribute(Shape $shape, Shape $memberShape, $value)
|
||||
{
|
||||
$namespace = $shape['xmlNamespace']['uri']
|
||||
? $shape['xmlNamespace']['uri']
|
||||
: '';
|
||||
$prefix = $shape['xmlNamespace']['prefix']
|
||||
? $shape['xmlNamespace']['prefix']
|
||||
: '';
|
||||
$namespace = $shape['xmlNamespace']['uri'] ?? '';
|
||||
$prefix = $shape['xmlNamespace']['prefix'] ?? '';
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= ':';
|
||||
}
|
||||
|
|
|
@ -45,14 +45,22 @@ class JsonBody
|
|||
* Builds the JSON body based on an array of arguments.
|
||||
*
|
||||
* @param Shape $shape Operation being constructed
|
||||
* @param array $args Associative array of arguments
|
||||
* @param array|string $args Associative array of arguments, or a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function build(Shape $shape, array $args)
|
||||
public function build(Shape $shape, array|string $args)
|
||||
{
|
||||
$result = json_encode($this->format($shape, $args));
|
||||
return $result == '[]' ? '{}' : $result;
|
||||
try {
|
||||
$result = json_encode($this->format($shape, $args), JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new InvalidJsonException(
|
||||
'Unable to encode JSON document ' . $shape->getName() . ': ' .
|
||||
$e->getMessage() . PHP_EOL
|
||||
);
|
||||
}
|
||||
|
||||
return $result === '[]' ? '{}' : $result;
|
||||
}
|
||||
|
||||
private function format(Shape $shape, $value)
|
||||
|
@ -60,7 +68,7 @@ class JsonBody
|
|||
switch ($shape['type']) {
|
||||
case 'structure':
|
||||
$data = [];
|
||||
if (isset($shape['document']) && $shape['document']) {
|
||||
if ($shape['document'] ?? false) {
|
||||
return $value;
|
||||
}
|
||||
foreach ($value as $k => $v) {
|
||||
|
|
|
@ -62,23 +62,26 @@ class JsonRpcSerializer
|
|||
$operationName = $command->getName();
|
||||
$operation = $this->api->getOperation($operationName);
|
||||
$commandArgs = $command->toArray();
|
||||
$body = $this->jsonFormatter->build($operation->getInput(), $commandArgs);
|
||||
$headers = [
|
||||
'X-Amz-Target' => $this->api->getMetadata('targetPrefix') . '.' . $operationName,
|
||||
'Content-Type' => $this->contentType
|
||||
];
|
||||
'Content-Type' => $this->contentType,
|
||||
'Content-Length' => strlen($body)
|
||||
];
|
||||
|
||||
if ($endpoint instanceof RulesetEndpoint) {
|
||||
$this->setEndpointV2RequestOptions($endpoint, $headers);
|
||||
}
|
||||
|
||||
$requestUri = $operation['http']['requestUri'] ?? null;
|
||||
$absoluteUri = str_ends_with($this->endpoint, '/')
|
||||
? $this->endpoint : $this->endpoint . $requestUri;
|
||||
|
||||
return new Request(
|
||||
$operation['http']['method'],
|
||||
$this->endpoint,
|
||||
$absoluteUri,
|
||||
$headers,
|
||||
$this->jsonFormatter->build(
|
||||
$operation->getInput(),
|
||||
$commandArgs
|
||||
)
|
||||
$body
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,8 @@ class QueryParamBuilder
|
|||
if (!$this->isFlat($shape)) {
|
||||
$locationName = $shape->getMember()['locationName'] ?: 'member';
|
||||
$prefix .= ".$locationName";
|
||||
} elseif ($name = $this->queryName($items)) {
|
||||
// flattened lists can also model a `locationName`
|
||||
} elseif ($name = $shape['locationName'] ?? $this->queryName($items)) {
|
||||
$parts = explode('.', $prefix);
|
||||
$parts[count($parts) - 1] = $name;
|
||||
$prefix = implode('.', $parts);
|
||||
|
|
|
@ -36,9 +36,7 @@ class QuerySerializer
|
|||
* containing "method", "uri", "headers", and "body" key value pairs.
|
||||
*
|
||||
* @param CommandInterface $command Command to serialize into a request.
|
||||
* @param $endpointProvider Provider used for dynamic endpoint resolution.
|
||||
* @param $clientArgs Client arguments used for dynamic endpoint resolution.
|
||||
*
|
||||
* @param null $endpoint Endpoint resolved using EndpointProviderV2
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function __invoke(
|
||||
|
@ -66,14 +64,17 @@ class QuerySerializer
|
|||
'Content-Length' => strlen($body),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
];
|
||||
$requestUri = $operation['http']['requestUri'] ?? null;
|
||||
|
||||
if ($endpoint instanceof RulesetEndpoint) {
|
||||
$this->setEndpointV2RequestOptions($endpoint, $headers);
|
||||
}
|
||||
$absoluteUri = str_ends_with($this->endpoint, '/')
|
||||
? $this->endpoint : $this->endpoint . $requestUri;
|
||||
|
||||
return new Request(
|
||||
'POST',
|
||||
$this->endpoint,
|
||||
$absoluteUri,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
|
|
|
@ -31,12 +31,11 @@ class RestJsonSerializer extends RestSerializer
|
|||
$this->jsonFormatter = $jsonFormatter ?: new JsonBody($api);
|
||||
}
|
||||
|
||||
protected function payload(StructureShape $member, array $value, array &$opts)
|
||||
protected function payload(StructureShape $member, array|string $value, array &$opts)
|
||||
{
|
||||
$body = isset($value) ?
|
||||
((string) $this->jsonFormatter->build($member, $value))
|
||||
: "{}";
|
||||
$opts['headers']['Content-Type'] = $this->contentType;
|
||||
$body = $this->jsonFormatter->build($member, $value);
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
$opts['body'] = $body;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace Aws\Api\Serializer;
|
||||
|
||||
use Aws\Api\ListShape;
|
||||
use Aws\Api\MapShape;
|
||||
use Aws\Api\Service;
|
||||
use Aws\Api\Operation;
|
||||
|
@ -10,11 +11,13 @@ use Aws\Api\TimestampShape;
|
|||
use Aws\CommandInterface;
|
||||
use Aws\EndpointV2\EndpointV2SerializerTrait;
|
||||
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
|
||||
use DateTimeInterface;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use GuzzleHttp\Psr7\UriResolver;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Serializes HTTP locations like header, uri, payload, etc...
|
||||
|
@ -22,10 +25,15 @@ use Psr\Http\Message\RequestInterface;
|
|||
*/
|
||||
abstract class RestSerializer
|
||||
{
|
||||
use EndpointV2SerializerTrait;
|
||||
private const TEMPLATE_STRING_REGEX = '/\{([^\}]+)\}/';
|
||||
|
||||
private static array $excludeContentType = [
|
||||
's3' => true,
|
||||
'glacier' => true
|
||||
];
|
||||
|
||||
/** @var Service */
|
||||
private $api;
|
||||
private Service $api;
|
||||
|
||||
/** @var Uri */
|
||||
private $endpoint;
|
||||
|
@ -33,9 +41,11 @@ abstract class RestSerializer
|
|||
/** @var bool */
|
||||
private $isUseEndpointV2;
|
||||
|
||||
use EndpointV2SerializerTrait;
|
||||
|
||||
/**
|
||||
* @param Service $api Service API description
|
||||
* @param string $endpoint Endpoint to connect to
|
||||
* @param Service $api Service API description
|
||||
* @param string $endpoint Endpoint to connect to
|
||||
*/
|
||||
public function __construct(Service $api, $endpoint)
|
||||
{
|
||||
|
@ -45,19 +55,18 @@ abstract class RestSerializer
|
|||
|
||||
/**
|
||||
* @param CommandInterface $command Command to serialize into a request.
|
||||
* @param $clientArgs Client arguments used for dynamic endpoint resolution.
|
||||
*
|
||||
* @param mixed|null $endpoint
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function __invoke(
|
||||
CommandInterface $command,
|
||||
$endpoint = null
|
||||
mixed $endpoint = null
|
||||
)
|
||||
{
|
||||
$operation = $this->api->getOperation($command->getName());
|
||||
$commandArgs = $command->toArray();
|
||||
$opts = $this->serialize($operation, $commandArgs);
|
||||
$headers = isset($opts['headers']) ? $opts['headers'] : [];
|
||||
$headers = $opts['headers'] ?? [];
|
||||
|
||||
if ($endpoint instanceof RulesetEndpoint) {
|
||||
$this->isUseEndpointV2 = true;
|
||||
|
@ -70,16 +79,16 @@ abstract class RestSerializer
|
|||
$operation['http']['method'],
|
||||
$uri,
|
||||
$headers,
|
||||
isset($opts['body']) ? $opts['body'] : null
|
||||
$opts['body'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a hash of request options for a payload body.
|
||||
*
|
||||
* @param StructureShape $member Member to serialize
|
||||
* @param array $value Value to serialize
|
||||
* @param array $opts Request options to modify.
|
||||
* @param StructureShape $member Member to serialize
|
||||
* @param array $value Value to serialize
|
||||
* @param array $opts Request options to modify.
|
||||
*/
|
||||
abstract protected function payload(
|
||||
StructureShape $member,
|
||||
|
@ -103,20 +112,20 @@ abstract class RestSerializer
|
|||
$location = $member['location'];
|
||||
if (!$payload && !$location) {
|
||||
$bodyMembers[$name] = $value;
|
||||
} elseif ($location == 'header') {
|
||||
} elseif ($location === 'header') {
|
||||
$this->applyHeader($name, $member, $value, $opts);
|
||||
} elseif ($location == 'querystring') {
|
||||
} elseif ($location === 'querystring') {
|
||||
$this->applyQuery($name, $member, $value, $opts);
|
||||
} elseif ($location == 'headers') {
|
||||
} elseif ($location === 'headers') {
|
||||
$this->applyHeaderMap($name, $member, $value, $opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($bodyMembers)) {
|
||||
$this->payload($operation->getInput(), $bodyMembers, $opts);
|
||||
$this->payload($input, $bodyMembers, $opts);
|
||||
} else if (!isset($opts['body']) && $this->hasPayloadParam($input, $payload)) {
|
||||
$this->payload($operation->getInput(), [], $opts);
|
||||
$this->payload($input, [], $opts);
|
||||
}
|
||||
|
||||
return $opts;
|
||||
|
@ -130,12 +139,32 @@ abstract class RestSerializer
|
|||
|
||||
$m = $input->getMember($name);
|
||||
|
||||
$type = $m->getType();
|
||||
if ($m['streaming'] ||
|
||||
($m['type'] == 'string' || $m['type'] == 'blob')
|
||||
($type === 'string' || $type === 'blob')
|
||||
) {
|
||||
// This path skips setting the content-type header usually done in
|
||||
// RestJsonSerializer and RestXmlSerializer.certain S3 and glacier
|
||||
// operations determine content type in Middleware::ContentType()
|
||||
if (!isset(self::$excludeContentType[$this->api->getServiceName()])) {
|
||||
switch ($type) {
|
||||
case 'string':
|
||||
$opts['headers']['Content-Type'] = 'text/plain';
|
||||
break;
|
||||
case 'blob':
|
||||
$opts['headers']['Content-Type'] = 'application/octet-stream';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$body = $args[$name];
|
||||
if (!$m['streaming'] && is_string($body)) {
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
}
|
||||
|
||||
// Streaming bodies or payloads that are strings are
|
||||
// always just a stream of data.
|
||||
$opts['body'] = Psr7\Utils::streamFor($args[$name]);
|
||||
$opts['body'] = Psr7\Utils::streamFor($body);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -144,13 +173,29 @@ abstract class RestSerializer
|
|||
|
||||
private function applyHeader($name, Shape $member, $value, array &$opts)
|
||||
{
|
||||
if ($member->getType() === 'timestamp') {
|
||||
$timestampFormat = !empty($member['timestampFormat'])
|
||||
? $member['timestampFormat']
|
||||
: 'rfc822';
|
||||
$value = TimestampShape::format($value, $timestampFormat);
|
||||
} elseif ($member->getType() === 'boolean') {
|
||||
$value = $value ? 'true' : 'false';
|
||||
// Handle lists by recursively applying header logic to each element
|
||||
if ($member instanceof ListShape) {
|
||||
$listMember = $member->getMember();
|
||||
$headerValues = [];
|
||||
|
||||
foreach ($value as $listValue) {
|
||||
$tempOpts = ['headers' => []];
|
||||
$this->applyHeader('temp', $listMember, $listValue, $tempOpts);
|
||||
$convertedValue = $tempOpts['headers']['temp'];
|
||||
$headerValues[] = $convertedValue;
|
||||
}
|
||||
|
||||
$value = $headerValues;
|
||||
} elseif (!is_null($value)) {
|
||||
switch ($member->getType()) {
|
||||
case 'timestamp':
|
||||
$timestampFormat = $member['timestampFormat'] ?? 'rfc822';
|
||||
$value = $this->formatTimestamp($value, $timestampFormat);
|
||||
break;
|
||||
case 'boolean':
|
||||
$value = $this->formatBoolean($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($member['jsonvalue']) {
|
||||
|
@ -183,148 +228,259 @@ abstract class RestSerializer
|
|||
$opts['query'] = isset($opts['query']) && is_array($opts['query'])
|
||||
? $opts['query'] + $value
|
||||
: $value;
|
||||
} elseif ($value !== null) {
|
||||
$type = $member->getType();
|
||||
if ($type === 'boolean') {
|
||||
$value = $value ? 'true' : 'false';
|
||||
} elseif ($type === 'timestamp') {
|
||||
$timestampFormat = !empty($member['timestampFormat'])
|
||||
? $member['timestampFormat']
|
||||
: 'iso8601';
|
||||
$value = TimestampShape::format($value, $timestampFormat);
|
||||
} elseif ($member instanceof ListShape) {
|
||||
$listMember = $member->getMember();
|
||||
$paramName = $member['locationName'] ?: $name;
|
||||
|
||||
foreach ($value as $listValue) {
|
||||
// Recursively call applyQuery for each list element
|
||||
$tempOpts = ['query' => []];
|
||||
$this->applyQuery('temp', $listMember, $listValue, $tempOpts);
|
||||
$opts['query'][$paramName][] = $tempOpts['query']['temp'];
|
||||
}
|
||||
} elseif (!is_null($value)) {
|
||||
switch ($member->getType()) {
|
||||
case 'timestamp':
|
||||
$timestampFormat = $member['timestampFormat'] ?? 'iso8601';
|
||||
$value = $this->formatTimestamp($value, $timestampFormat);
|
||||
break;
|
||||
case 'boolean':
|
||||
$value = $this->formatBoolean($value);
|
||||
break;
|
||||
}
|
||||
|
||||
$opts['query'][$member['locationName'] ?: $name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildEndpoint(Operation $operation, array $args, array $opts)
|
||||
private function buildEndpoint(
|
||||
Operation $operation,
|
||||
array $args,
|
||||
array $opts
|
||||
): UriInterface
|
||||
{
|
||||
// Expand `requestUri` field members
|
||||
$relativeUri = $this->expandUriTemplate($operation, $args);
|
||||
|
||||
// Add query members to relativeUri
|
||||
if (!empty($opts['query'])) {
|
||||
$relativeUri = $this->appendQuery($opts['query'], $relativeUri);
|
||||
}
|
||||
|
||||
// Special case - S3 keys that need path preservation
|
||||
if ($this->api->getServiceName() === 's3'
|
||||
&& isset($args['Key'])
|
||||
&& $this->shouldPreservePath($args['Key'])
|
||||
) {
|
||||
return new Uri($this->endpoint . $relativeUri);
|
||||
}
|
||||
|
||||
return $this->resolveUri($relativeUri, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands `requestUri` members
|
||||
*
|
||||
* @param Operation $operation
|
||||
* @param array $args
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function expandUriTemplate(Operation $operation, array $args): string
|
||||
{
|
||||
$serviceName = $this->api->getServiceName();
|
||||
// Create an associative array of variable definitions used in expansions
|
||||
$varDefinitions = $this->getVarDefinitions($operation, $args);
|
||||
|
||||
$relative = preg_replace_callback(
|
||||
'/\{([^\}]+)\}/',
|
||||
function (array $matches) use ($varDefinitions) {
|
||||
$isGreedy = substr($matches[1], -1, 1) == '+';
|
||||
$k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
|
||||
if (!isset($varDefinitions[$k])) {
|
||||
return preg_replace_callback(
|
||||
self::TEMPLATE_STRING_REGEX,
|
||||
static function (array $matches) use ($varDefinitions) {
|
||||
$isGreedy = str_ends_with($matches[1], '+');
|
||||
$varName = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
|
||||
|
||||
if (!isset($varDefinitions[$varName])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = $varDefinitions[$varName];
|
||||
|
||||
if ($isGreedy) {
|
||||
return str_replace('%2F', '/', rawurlencode($varDefinitions[$k]));
|
||||
return str_replace('%2F', '/', rawurlencode($value));
|
||||
}
|
||||
|
||||
return rawurlencode($varDefinitions[$k]);
|
||||
return rawurlencode($value);
|
||||
},
|
||||
$operation['http']['requestUri']
|
||||
);
|
||||
}
|
||||
|
||||
// Add the query string variables or appending to one if needed.
|
||||
if (!empty($opts['query'])) {
|
||||
$relative = $this->appendQuery($opts['query'], $relative);
|
||||
}
|
||||
|
||||
$path = $this->endpoint->getPath();
|
||||
|
||||
if ($this->isUseEndpointV2 && $serviceName === 's3') {
|
||||
if (substr($path, -1) === '/' && $relative[0] === '/') {
|
||||
$path = rtrim($path, '/');
|
||||
}
|
||||
$relative = $path . $relative;
|
||||
|
||||
if (strpos($relative, '../') !== false
|
||||
|| substr($relative, -2) === '..'
|
||||
) {
|
||||
if ($relative[0] !== '/') {
|
||||
$relative = '/' . $relative;
|
||||
/**
|
||||
* Checks for path-like key names. If detected, traditional
|
||||
* URI resolution is bypassed.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldPreservePath(string $key): bool
|
||||
{
|
||||
// Keys with dot segments
|
||||
if (str_contains($key, '.')) {
|
||||
$segments = explode('/', $key);
|
||||
foreach ($segments as $segment) {
|
||||
if ($segment === '.' || $segment === '..') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new Uri($this->endpoint->withPath('') . $relative);
|
||||
}
|
||||
}
|
||||
|
||||
if (((!empty($relative) && $relative !== '/')
|
||||
&& !$this->isUseEndpointV2)
|
||||
|| (isset($serviceName) && str_starts_with($serviceName, 'geo-'))
|
||||
) {
|
||||
$this->normalizePath($path);
|
||||
// Keys starting with slash
|
||||
if (str_starts_with($key, '/')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If endpoint has path, remove leading '/' to preserve URI resolution.
|
||||
if ($path && $relative[0] === '/') {
|
||||
$relative = substr($relative, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $relativeUri
|
||||
* @param array $opts
|
||||
*
|
||||
* @return UriInterface
|
||||
*/
|
||||
private function resolveUri(string $relativeUri, array $opts): UriInterface
|
||||
{
|
||||
$basePath = $this->endpoint->getPath();
|
||||
|
||||
// Only process if we have a non-empty base path
|
||||
if (!empty($basePath) && $basePath !== '/') {
|
||||
// if relative is just '/', we want just the base path without trailing slash
|
||||
if ($relativeUri === '/' || empty($relativeUri)) {
|
||||
// Remove trailing slash if present
|
||||
return $this->endpoint->withPath(rtrim($basePath, '/'));
|
||||
}
|
||||
|
||||
// if relative is '/?query', we want base path without trailing slash + query
|
||||
// for now, this is only seen with S3 GetBucketLocation after processing the model
|
||||
if (empty($opts['query'])
|
||||
&& str_starts_with($relativeUri, '/?')
|
||||
) {
|
||||
$query = substr($relativeUri, 2); // Remove '/?'
|
||||
return $this->endpoint->withQuery($query);
|
||||
}
|
||||
|
||||
// Ensure base path has trailing slash
|
||||
if (!str_ends_with($basePath, '/')) {
|
||||
$this->endpoint = $this->endpoint->withPath($basePath . '/');
|
||||
}
|
||||
|
||||
// Remove leading slash from relative path to make it relative
|
||||
if (str_starts_with($relativeUri, '/')) {
|
||||
$relativeUri = substr($relativeUri, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//Append path to endpoint when leading '//...'
|
||||
// present as uri cannot be properly resolved
|
||||
if ($this->isUseEndpointV2 && strpos($relative, '//') === 0) {
|
||||
return new Uri($this->endpoint . $relative);
|
||||
}
|
||||
|
||||
// Expand path place holders using Amazon's slightly different URI
|
||||
// template syntax.
|
||||
return UriResolver::resolve($this->endpoint, new Uri($relative));
|
||||
return UriResolver::resolve($this->endpoint, new Uri($relativeUri));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StructureShape $input
|
||||
* @param $payload
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasPayloadParam(StructureShape $input, $payload)
|
||||
{
|
||||
if ($payload) {
|
||||
$potentiallyEmptyTypes = ['blob','string'];
|
||||
if ($this->api->getMetadata('protocol') == 'rest-xml') {
|
||||
if ($this->api->getProtocol() === 'rest-xml') {
|
||||
$potentiallyEmptyTypes[] = 'structure';
|
||||
}
|
||||
|
||||
$payloadMember = $input->getMember($payload);
|
||||
if (in_array($payloadMember['type'], $potentiallyEmptyTypes)) {
|
||||
//unions may also be empty/unset
|
||||
if (!empty($payloadMember['union'])
|
||||
|| in_array($payloadMember['type'], $potentiallyEmptyTypes)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($input->getMembers() as $member) {
|
||||
if (!isset($member['location'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function appendQuery($query, $endpoint)
|
||||
/**
|
||||
* @param $query
|
||||
* @param $relativeUri
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function appendQuery($query, $relativeUri): string
|
||||
{
|
||||
$append = Psr7\Query::build($query);
|
||||
return $endpoint .= strpos($endpoint, '?') !== false ? "&{$append}" : "?{$append}";
|
||||
return $relativeUri
|
||||
. (str_contains($relativeUri, '?') ? "&{$append}" : "?{$append}");
|
||||
}
|
||||
|
||||
private function getVarDefinitions($command, $args)
|
||||
/**
|
||||
* @param CommandInterface $command
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getVarDefinitions(
|
||||
Operation $operation,
|
||||
array $args
|
||||
): array
|
||||
{
|
||||
$varDefinitions = [];
|
||||
|
||||
foreach ($command->getInput()->getMembers() as $name => $member) {
|
||||
if ($member['location'] == 'uri') {
|
||||
$varDefinitions[$member['locationName'] ?: $name] =
|
||||
isset($args[$name])
|
||||
? $args[$name]
|
||||
: null;
|
||||
foreach ($operation->getInput()->getMembers() as $name => $member) {
|
||||
if ($member['location'] === 'uri') {
|
||||
$value = $args[$name] ?? null;
|
||||
if (!is_null($value)) {
|
||||
switch ($member->getType()) {
|
||||
case 'timestamp':
|
||||
$timestampFormat = $member['timestampFormat'] ?? 'iso8601';
|
||||
$value = $this->formatTimestamp($value, $timestampFormat);
|
||||
break;
|
||||
case 'boolean':
|
||||
$value = $this->formatBoolean($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$varDefinitions[$member['locationName'] ?: $name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $varDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends trailing slash to non-empty paths with at least one segment
|
||||
* to ensure proper URI resolution
|
||||
* @param DateTimeInterface|string|int $value
|
||||
* @param string $timestampFormat
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
private function normalizePath(string $path): void
|
||||
private function formatTimestamp(
|
||||
DateTimeInterface|string|int $value,
|
||||
string $timestampFormat
|
||||
): string
|
||||
{
|
||||
if (!empty($path) && $path !== '/' && substr($path, -1) !== '/') {
|
||||
$this->endpoint = $this->endpoint->withPath($path . '/');
|
||||
}
|
||||
return TimestampShape::format($value, $timestampFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function formatBoolean($value): string
|
||||
{
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ class RestXmlSerializer extends RestSerializer
|
|||
protected function payload(StructureShape $member, array $value, array &$opts)
|
||||
{
|
||||
$opts['headers']['Content-Type'] = 'application/xml';
|
||||
$opts['body'] = $this->getXmlBody($member, $value);
|
||||
$body = $this->getXmlBody($member, $value);
|
||||
$opts['headers']['Content-Length'] = strlen($body);
|
||||
$opts['body'] = $body;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,8 +14,8 @@ use XMLWriter;
|
|||
*/
|
||||
class XmlBody
|
||||
{
|
||||
/** @var \Aws\Api\Service */
|
||||
private $api;
|
||||
/** @var Service */
|
||||
private Service $api;
|
||||
|
||||
/**
|
||||
* @param Service $api API being used to create the XML body.
|
||||
|
@ -38,7 +38,10 @@ class XmlBody
|
|||
$xml = new XMLWriter();
|
||||
$xml->openMemory();
|
||||
$xml->startDocument('1.0', 'UTF-8');
|
||||
$this->format($shape, $shape['locationName'] ?: $shape['name'], $args, $xml);
|
||||
|
||||
$rootElementName = $this->determineRootElementName($shape);
|
||||
|
||||
$this->format($shape, $rootElementName, $args, $xml);
|
||||
$xml->endDocument();
|
||||
|
||||
return $xml->outputMemory();
|
||||
|
@ -51,7 +54,7 @@ class XmlBody
|
|||
if ($ns = $shape['xmlNamespace']) {
|
||||
$xml->writeAttribute(
|
||||
isset($ns['prefix']) ? "xmlns:{$ns['prefix']}" : 'xmlns',
|
||||
$shape['xmlNamespace']['uri']
|
||||
$ns['uri']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -93,9 +96,19 @@ class XmlBody
|
|||
$this->startElement($shape, $name, $xml);
|
||||
|
||||
foreach ($this->getStructureMembers($shape, $value) as $k => $definition) {
|
||||
// Default to member name
|
||||
$elementName = $k;
|
||||
|
||||
// Only use locationName for non-structure members
|
||||
if (!($definition['member'] instanceof StructureShape)
|
||||
&& $definition['member']['locationName']
|
||||
) {
|
||||
$elementName = $definition['member']['locationName'];
|
||||
}
|
||||
|
||||
$this->format(
|
||||
$definition['member'],
|
||||
$definition['member']['locationName'] ?: $k,
|
||||
$elementName,
|
||||
$definition['value'],
|
||||
$xml
|
||||
);
|
||||
|
@ -157,11 +170,13 @@ class XmlBody
|
|||
array $value,
|
||||
XMLWriter $xml
|
||||
) {
|
||||
$xmlEntry = $shape['flattened'] ? $shape['locationName'] : 'entry';
|
||||
$xmlEntry = $shape['flattened'] ? $name : 'entry';
|
||||
$xmlKey = $shape->getKey()['locationName'] ?: 'key';
|
||||
$xmlValue = $shape->getValue()['locationName'] ?: 'value';
|
||||
|
||||
$this->startElement($shape, $name, $xml);
|
||||
if (!$shape['flattened']) {
|
||||
$this->startElement($shape, $name, $xml);
|
||||
}
|
||||
|
||||
foreach ($value as $key => $v) {
|
||||
$this->startElement($shape, $xmlEntry, $xml);
|
||||
|
@ -170,7 +185,9 @@ class XmlBody
|
|||
$xml->endElement();
|
||||
}
|
||||
|
||||
$xml->endElement();
|
||||
if (!$shape['flattened']) {
|
||||
$xml->endElement();
|
||||
}
|
||||
}
|
||||
|
||||
private function add_blob(Shape $shape, $name, $value, XMLWriter $xml)
|
||||
|
@ -217,4 +234,23 @@ class XmlBody
|
|||
$this->defaultShape($shape, $name, $value, $xml);
|
||||
}
|
||||
}
|
||||
|
||||
private function determineRootElementName(Shape $shape): string
|
||||
{
|
||||
$shapeName = $shape->getName();
|
||||
|
||||
// Look up the shape definition first
|
||||
if ($shapeName && $shapeMap = $shape->getShapeMap()) {
|
||||
if (isset($shapeMap[$shapeName]['locationName'])) {
|
||||
return $shapeMap[$shapeName]['locationName'];
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to shape's current locationName
|
||||
if ($shape['locationName']) {
|
||||
return $shape['locationName'];
|
||||
}
|
||||
|
||||
return $shapeName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Aws\Api;
|
|||
/**
|
||||
* Builds shape based on shape references.
|
||||
*/
|
||||
class ShapeMap
|
||||
class ShapeMap implements \ArrayAccess
|
||||
{
|
||||
/** @var array */
|
||||
private $definitions;
|
||||
|
@ -65,4 +65,45 @@ class ShapeMap
|
|||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return isset($this->definitions[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->definitions[$offset] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
'ShapeMap is read-only and cannot be modified.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
'ShapeMap is read-only and cannot be modified.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,32 @@ class StructureShape extends Shape
|
|||
return $members[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to look up the shape's original definition.
|
||||
* ShapeMap::resolve() merges properties from both
|
||||
* member and target shape definitions, causing certain
|
||||
* properties like `locationName` to be overwritten.
|
||||
*
|
||||
* @return ShapeMap
|
||||
* @internal This method is for internal use only and should not be used
|
||||
* by external code. It may be changed or removed without notice.
|
||||
*/
|
||||
public function getShapeMap(): ShapeMap
|
||||
{
|
||||
return $this->shapeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to look up a shape's original definition.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getOriginalDefinition(string $name): ?array
|
||||
{
|
||||
return $this->shapeMap[$name] ?? null;
|
||||
}
|
||||
|
||||
private function generateMembersHash()
|
||||
{
|
||||
|
|
|
@ -13,9 +13,9 @@ interface AuthSchemeResolverInterface
|
|||
* Selects an auth scheme for request signing.
|
||||
*
|
||||
* @param array $authSchemes a priority-ordered list of authentication schemes.
|
||||
* @param IdentityInterface $identity Credentials to be used in request signing.
|
||||
* @param array $args
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function selectAuthScheme(
|
||||
array $authSchemes,
|
||||
|
|
|
@ -28,38 +28,50 @@ class AuthSelectionMiddleware
|
|||
/** @var Service */
|
||||
private $api;
|
||||
|
||||
/** @var array|null */
|
||||
private ?array $configuredAuthSchemes;
|
||||
|
||||
/**
|
||||
* Create a middleware wrapper function
|
||||
*
|
||||
* @param AuthSchemeResolverInterface $authResolver
|
||||
* @param Service $api
|
||||
* @param array|null $configuredAuthSchemes
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
public static function wrap(
|
||||
AuthSchemeResolverInterface $authResolver,
|
||||
Service $api
|
||||
Service $api,
|
||||
?array $configuredAuthSchemes
|
||||
): Closure
|
||||
{
|
||||
return function (callable $handler) use ($authResolver, $api) {
|
||||
return new self($handler, $authResolver, $api);
|
||||
return function (callable $handler) use (
|
||||
$authResolver,
|
||||
$api,
|
||||
$configuredAuthSchemes
|
||||
) {
|
||||
return new self($handler, $authResolver, $api, $configuredAuthSchemes);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $nextHandler
|
||||
* @param $authResolver
|
||||
* @param callable $identityProvider
|
||||
* @param AuthSchemeResolverInterface $authResolver
|
||||
* @param Service $api
|
||||
* @param array|null $configuredAuthSchemes
|
||||
*/
|
||||
public function __construct(
|
||||
callable $nextHandler,
|
||||
AuthSchemeResolverInterface $authResolver,
|
||||
Service $api
|
||||
Service $api,
|
||||
?array $configuredAuthSchemes = null
|
||||
)
|
||||
{
|
||||
$this->nextHandler = $nextHandler;
|
||||
$this->authResolver = $authResolver;
|
||||
$this->api = $api;
|
||||
$this->configuredAuthSchemes = $configuredAuthSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,21 +98,62 @@ class AuthSelectionMiddleware
|
|||
}
|
||||
|
||||
try {
|
||||
$selectedAuthScheme = $resolver->selectAuthScheme(
|
||||
$authSchemeList = $this->buildAuthSchemeList(
|
||||
$resolvableAuth,
|
||||
$command['@context']['auth_scheme_preference']
|
||||
?? null,
|
||||
);
|
||||
$selectedAuthScheme = $resolver->selectAuthScheme(
|
||||
$authSchemeList,
|
||||
['unsigned_payload' => $unsignedPayload]
|
||||
);
|
||||
} catch (UnresolvedAuthSchemeException $e) {
|
||||
|
||||
if (!empty($selectedAuthScheme)) {
|
||||
$command['@context']['signature_version'] = $selectedAuthScheme;
|
||||
}
|
||||
} catch (UnresolvedAuthSchemeException $ignored) {
|
||||
// There was an error resolving auth
|
||||
// The signature version will fall back to the modeled `signatureVersion`
|
||||
// or auth schemes resolved during endpoint resolution
|
||||
}
|
||||
|
||||
if (!empty($selectedAuthScheme)) {
|
||||
$command['@context']['signature_version'] = $selectedAuthScheme;
|
||||
}
|
||||
}
|
||||
|
||||
return $nextHandler($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prioritizes auth schemes according to user preference order.
|
||||
* User-preferred schemes that are available will be placed first,
|
||||
* followed by remaining available schemes.
|
||||
*
|
||||
* @param array $resolvableAuthSchemeList Available auth schemes
|
||||
* @param array|null $commandConfiguredAuthSchemes Command-level preferences (overrides config)
|
||||
*
|
||||
* @return array Reordered auth schemes with user preferences first
|
||||
*/
|
||||
private function buildAuthSchemeList(
|
||||
array $resolvableAuthSchemeList,
|
||||
?array $commandConfiguredAuthSchemes,
|
||||
): array
|
||||
{
|
||||
$userConfiguredAuthSchemes = $commandConfiguredAuthSchemes
|
||||
?? $this->configuredAuthSchemes;
|
||||
|
||||
if (empty($userConfiguredAuthSchemes)) {
|
||||
return $resolvableAuthSchemeList;
|
||||
}
|
||||
|
||||
$prioritizedAuthSchemes = array_intersect(
|
||||
$userConfiguredAuthSchemes,
|
||||
$resolvableAuthSchemeList
|
||||
);
|
||||
|
||||
// Get remaining schemes not in user preferences
|
||||
$remainingAuthSchemes = array_diff(
|
||||
$resolvableAuthSchemeList,
|
||||
$prioritizedAuthSchemes
|
||||
);
|
||||
|
||||
return array_merge($prioritizedAuthSchemes, $remainingAuthSchemes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@ class AwsClient implements AwsClientInterface
|
|||
if ($this->isUseEndpointV2()) {
|
||||
$this->addEndpointV2Middleware();
|
||||
}
|
||||
$this->addAuthSelectionMiddleware();
|
||||
$this->addAuthSelectionMiddleware($config);
|
||||
|
||||
if (!is_null($this->api->getMetadata('awsQueryCompatible'))) {
|
||||
$this->addQueryCompatibleInputMiddleware($this->api);
|
||||
|
@ -303,6 +303,12 @@ class AwsClient implements AwsClientInterface
|
|||
return $fn();
|
||||
}
|
||||
|
||||
public function getToken()
|
||||
{
|
||||
$fn = $this->tokenProvider;
|
||||
return $fn();
|
||||
}
|
||||
|
||||
|
||||
public function getEndpoint()
|
||||
{
|
||||
|
@ -589,14 +595,15 @@ class AwsClient implements AwsClientInterface
|
|||
);
|
||||
}
|
||||
|
||||
private function addAuthSelectionMiddleware()
|
||||
private function addAuthSelectionMiddleware(array $args)
|
||||
{
|
||||
$list = $this->getHandlerList();
|
||||
|
||||
$list->prependBuild(
|
||||
AuthSelectionMiddleware::wrap(
|
||||
$this->authSchemeResolver,
|
||||
$this->getApi()
|
||||
$this->getApi(),
|
||||
$args['auth_scheme_preference'] ?? null
|
||||
),
|
||||
'auth-selection'
|
||||
);
|
||||
|
|
|
@ -34,6 +34,7 @@ use Aws\Retry\ConfigurationProvider as RetryConfigProvider;
|
|||
use Aws\Signature\SignatureProvider;
|
||||
use Aws\Token\Token;
|
||||
use Aws\Token\TokenInterface;
|
||||
use Aws\Token\BedrockTokenProvider;
|
||||
use Aws\Token\TokenProvider;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use InvalidArgumentException as IAE;
|
||||
|
@ -206,6 +207,13 @@ class ClientResolver
|
|||
'fn' => [__CLASS__, '_apply_credentials'],
|
||||
'default' => [__CLASS__, '_default_credential_provider'],
|
||||
],
|
||||
'auth_scheme_preference' => [
|
||||
'type' => 'value',
|
||||
'valid' => ['string', 'array'],
|
||||
'doc' => 'Comma-separated list of authentication scheme preferences in priority order. Configure via environment variable `AWS_AUTH_SCHEME_PREFERENCE`, INI config file `auth_scheme_preference`, or client constructor parameter `auth_scheme_preference` (string or array).\nExample: `AWS_AUTH_SCHEME_PREFERENCE=aws.auth#sigv4a,aws.auth#sigv4,smithy.api#httpBearerAuth`',
|
||||
'default' => self::DEFAULT_FROM_ENV_INI,
|
||||
'fn' => [__CLASS__, '_apply_auth_scheme_preference'],
|
||||
],
|
||||
'token' => [
|
||||
'type' => 'value',
|
||||
'valid' => [TokenInterface::class, CacheInterface::class, 'array', 'bool', 'callable'],
|
||||
|
@ -704,8 +712,17 @@ class ClientResolver
|
|||
}
|
||||
}
|
||||
|
||||
public static function _default_token_provider(array $args)
|
||||
public static function _default_token_provider(array &$args)
|
||||
{
|
||||
if (($args['config']['signing_name'] ?? '') === 'bedrock') {
|
||||
// Checks for env value, if present, sets auth_scheme_preference
|
||||
// to bearer auth and returns a provider
|
||||
$provider = BedrockTokenProvider::createIfAvailable($args);
|
||||
if (!is_null($provider)) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return TokenProvider::defaultProvider($args);
|
||||
}
|
||||
|
||||
|
@ -1122,6 +1139,32 @@ class ClientResolver
|
|||
return new AuthSchemeResolver($args['credentials'], $args['token']);
|
||||
}
|
||||
|
||||
public static function _apply_auth_scheme_preference(
|
||||
string|array|null &$value,
|
||||
array &$args
|
||||
): void
|
||||
{
|
||||
// Not provided user's preference auth scheme list
|
||||
if (empty($value)) {
|
||||
$value = null;
|
||||
$args['config']['auth_scheme_preference'] = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize it as an array
|
||||
if (is_string($value)) {
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
|
||||
// Let`s trim each value to remove break lines, spaces and/or tabs
|
||||
foreach ($value as &$val) {
|
||||
$val = trim($val);
|
||||
}
|
||||
|
||||
// Assign user's preferred auth scheme list
|
||||
$args['auth_scheme_preference'] = $value;
|
||||
}
|
||||
|
||||
public static function _default_signature_version(array &$args)
|
||||
{
|
||||
if (isset($args['config']['signature_version'])) {
|
||||
|
|
|
@ -68,7 +68,7 @@ class ConfigurationResolver
|
|||
*
|
||||
* @return null | mixed
|
||||
*/
|
||||
public static function env($key, $expectedType)
|
||||
public static function env($key, $expectedType = 'string')
|
||||
{
|
||||
// Use config from environment variables, if available
|
||||
$envValue = getenv(self::$envPrefix . strtoupper($key));
|
||||
|
@ -203,6 +203,7 @@ class ConfigurationResolver
|
|||
) {
|
||||
$value = intVal($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ class CredentialProvider
|
|||
const ENV_SESSION = 'AWS_SESSION_TOKEN';
|
||||
const ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE';
|
||||
const ENV_SHARED_CREDENTIALS_FILE = 'AWS_SHARED_CREDENTIALS_FILE';
|
||||
public const REFRESH_WINDOW = 60;
|
||||
|
||||
/**
|
||||
* Create a default credential provider that
|
||||
|
@ -224,10 +225,14 @@ class CredentialProvider
|
|||
return $creds;
|
||||
}
|
||||
|
||||
// Refresh expired credentials.
|
||||
if (!$creds->isExpired()) {
|
||||
// Check if credentials are expired or will expire in 1 minute
|
||||
$needsRefresh = $creds->getExpiration() - time() <= self::REFRESH_WINDOW;
|
||||
|
||||
// Refresh if expired or expiring soon
|
||||
if (!$needsRefresh && !$creds->isExpired()) {
|
||||
return $creds;
|
||||
}
|
||||
|
||||
// Refresh the result and forward the promise.
|
||||
return $result = $provider($creds);
|
||||
})
|
||||
|
|
|
@ -142,8 +142,12 @@ final class Middleware
|
|||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function signer(callable $credProvider, callable $signatureFunction, $tokenProvider = null, $config = [])
|
||||
{
|
||||
public static function signer(
|
||||
callable $credProvider,
|
||||
callable $signatureFunction,
|
||||
$tokenProvider = null,
|
||||
$config = []
|
||||
) {
|
||||
return function (callable $handler) use ($signatureFunction, $credProvider, $tokenProvider, $config) {
|
||||
return function (
|
||||
CommandInterface $command,
|
||||
|
|
|
@ -51,9 +51,7 @@ class RequestCompressionMiddleware
|
|||
}
|
||||
$nextHandler = $this->nextHandler;
|
||||
$operation = $this->api->getOperation($command->getName());
|
||||
$compressionInfo = isset($operation['requestcompression'])
|
||||
? $operation['requestcompression']
|
||||
: null;
|
||||
$compressionInfo = $operation['requestcompression'] ?? null;
|
||||
|
||||
if (!$this->shouldCompressRequestBody(
|
||||
$compressionInfo,
|
||||
|
@ -87,8 +85,12 @@ class RequestCompressionMiddleware
|
|||
$body = $request->getBody()->getContents();
|
||||
$compressedBody = $fn($body);
|
||||
|
||||
return $request->withBody(Psr7\Utils::streamFor($compressedBody))
|
||||
->withHeader('content-encoding', $this->encoding);
|
||||
$request = $request->withBody(Psr7\Utils::streamFor($compressedBody));
|
||||
if ($request->hasHeader('Content-Encoding')) {
|
||||
return $request->withAddedHeader('Content-Encoding', $this->encoding);
|
||||
}
|
||||
|
||||
return $request->withHeader('Content-Encoding', $this->encoding);
|
||||
}
|
||||
|
||||
private function determineEncoding()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace Aws\S3;
|
||||
|
||||
use Aws\CommandInterface;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -16,22 +17,36 @@ class BucketEndpointMiddleware
|
|||
{
|
||||
private static $exclusions = ['GetBucketLocation' => true];
|
||||
private $nextHandler;
|
||||
private bool $useEndpointV2;
|
||||
private ?string $endpoint;
|
||||
|
||||
/**
|
||||
* Create a middleware wrapper function.
|
||||
*
|
||||
* @param bool $useEndpointV2
|
||||
* @param string|null $endpoint
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function wrap()
|
||||
public static function wrap(
|
||||
bool $useEndpointV2 = false,
|
||||
?string $endpoint = null
|
||||
): callable
|
||||
{
|
||||
return function (callable $handler) {
|
||||
return new self($handler);
|
||||
return function (callable $handler) use ($useEndpointV2, $endpoint) {
|
||||
return new self($handler, $useEndpointV2, $endpoint);
|
||||
};
|
||||
}
|
||||
|
||||
public function __construct(callable $nextHandler)
|
||||
public function __construct(
|
||||
callable $nextHandler,
|
||||
bool $useEndpointV2,
|
||||
?string $endpoint = null
|
||||
)
|
||||
{
|
||||
$this->nextHandler = $nextHandler;
|
||||
$this->useEndpointV2 = $useEndpointV2;
|
||||
$this->endpoint = $endpoint;
|
||||
}
|
||||
|
||||
public function __invoke(CommandInterface $command, RequestInterface $request)
|
||||
|
@ -47,74 +62,50 @@ class BucketEndpointMiddleware
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a one-time removal of Bucket from path, then if
|
||||
* the bucket name is duplicated in the path, performs additional
|
||||
* removal which is dependent on the number of occurrences of the bucket
|
||||
* name in a path-like format in the key name.
|
||||
* @param string $path
|
||||
* @param string $bucket
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function removeBucketFromPath($path, $bucket, $key)
|
||||
private function removeBucketFromPath(string $path, string $bucket): string
|
||||
{
|
||||
$occurrencesInKey = $this->getBucketNameOccurrencesInKey($key, $bucket);
|
||||
do {
|
||||
$len = strlen($bucket) + 1;
|
||||
if (substr($path, 0, $len) === "/{$bucket}") {
|
||||
$path = substr($path, $len);
|
||||
}
|
||||
} while (substr_count($path, "/{$bucket}") > $occurrencesInKey + 1);
|
||||
$len = strlen($bucket) + 1;
|
||||
if (str_starts_with($path, "/{$bucket}")) {
|
||||
$path = substr($path, $len);
|
||||
}
|
||||
|
||||
return $path ?: '/';
|
||||
}
|
||||
|
||||
private function removeDuplicateBucketFromHost($host, $bucket)
|
||||
{
|
||||
if (substr_count($host, $bucket) > 1) {
|
||||
while (strpos($host, "{$bucket}.{$bucket}") === 0) {
|
||||
$hostArr = explode('.', $host);
|
||||
array_shift($hostArr);
|
||||
$host = implode('.', $hostArr);
|
||||
}
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
|
||||
private function getBucketNameOccurrencesInKey($key, $bucket)
|
||||
{
|
||||
$occurrences = 0;
|
||||
if (empty($key)) {
|
||||
return $occurrences;
|
||||
}
|
||||
|
||||
$segments = explode('/', $key);
|
||||
foreach($segments as $segment) {
|
||||
if (strpos($segment, $bucket) === 0) {
|
||||
$occurrences++;
|
||||
}
|
||||
}
|
||||
return $occurrences;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param CommandInterface $command
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
private function modifyRequest(
|
||||
RequestInterface $request,
|
||||
CommandInterface $command
|
||||
) {
|
||||
$key = isset($command['Key']) ? $command['Key'] : null;
|
||||
): RequestInterface
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
$path = $uri->getPath();
|
||||
$host = $uri->getHost();
|
||||
$bucket = $command['Bucket'];
|
||||
$path = $this->removeBucketFromPath($path, $bucket, $key);
|
||||
$host = $this->removeDuplicateBucketFromHost($host, $bucket);
|
||||
|
||||
if ($this->useEndpointV2 && !empty($this->endpoint)) {
|
||||
// V2 provider adds bucket name to host by default
|
||||
// preserve original host
|
||||
$host = (new Uri($this->endpoint))->getHost();
|
||||
}
|
||||
|
||||
$path = $this->removeBucketFromPath($path, $bucket);
|
||||
|
||||
// Modify the Key to make sure the key is encoded, but slashes are not.
|
||||
if ($key) {
|
||||
if ($command['Key']) {
|
||||
$path = S3Client::encodeKey(rawurldecode($path));
|
||||
}
|
||||
|
||||
return $request->withUri(
|
||||
$uri->withHost($host)
|
||||
->withPath($path)
|
||||
);
|
||||
return $request->withUri($uri->withPath($path)->withHost($host));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @method \GuzzleHttp\Promise\Promise copyObjectAsync(array $args = [])
|
||||
* @method \Aws\Result createBucket(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketAsync(array $args = [])
|
||||
* @method \Aws\Result createBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result createBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result createMultipartUpload(array $args = [])
|
||||
|
@ -61,6 +63,8 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @method \GuzzleHttp\Promise\Promise deleteBucketInventoryConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketLifecycle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketLifecycleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetricsConfiguration(array $args = [])
|
||||
|
@ -105,6 +109,8 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @method \GuzzleHttp\Promise\Promise getBucketLocationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketLogging(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketLoggingAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetricsConfiguration(array $args = [])
|
||||
|
@ -233,6 +239,10 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @method \GuzzleHttp\Promise\Promise restoreObjectAsync(array $args = [])
|
||||
* @method \Aws\Result selectObjectContent(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise selectObjectContentAsync(array $args = [])
|
||||
* @method \Aws\Result updateBucketMetadataInventoryTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBucketMetadataInventoryTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result updateBucketMetadataJournalTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBucketMetadataJournalTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result uploadPart(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise uploadPartAsync(array $args = [])
|
||||
* @method \Aws\Result uploadPartCopy(array $args = [])
|
||||
|
@ -422,6 +432,7 @@ class S3Client extends AwsClient implements S3ClientInterface
|
|||
$this->addBuiltIns($args);
|
||||
parent::__construct($args);
|
||||
$stack = $this->getHandlerList();
|
||||
$config = $this->getConfig();
|
||||
$stack->appendInit(SSECMiddleware::wrap($this->getEndpoint()->getScheme()), 's3.ssec');
|
||||
$stack->appendBuild(
|
||||
ApplyChecksumMiddleware::wrap($this->getApi(), $this->getConfig()),
|
||||
|
@ -433,7 +444,9 @@ class S3Client extends AwsClient implements S3ClientInterface
|
|||
);
|
||||
|
||||
if ($this->getConfig('bucket_endpoint')) {
|
||||
$stack->appendBuild(BucketEndpointMiddleware::wrap(), 's3.bucket_endpoint');
|
||||
$stack->appendBuild(BucketEndpointMiddleware::wrap(
|
||||
$this->isUseEndpointV2(), $args['endpoint'] ?? null), 's3.bucket_endpoint'
|
||||
);
|
||||
} elseif (!$this->isUseEndpointV2()) {
|
||||
$stack->appendBuild(
|
||||
S3EndpointMiddleware::wrap(
|
||||
|
@ -916,6 +929,10 @@ class S3Client extends AwsClient implements S3ClientInterface
|
|||
$requestUri = str_replace('/{Bucket}', '/', $requestUri);
|
||||
} else {
|
||||
$requestUri = str_replace('/{Bucket}', '', $requestUri);
|
||||
// If we're left with just a query string, prepend '/'
|
||||
if (str_starts_with($requestUri, '?')) {
|
||||
$requestUri = '/' . $requestUri;
|
||||
}
|
||||
}
|
||||
$operation['http']['requestUri'] = $requestUri;
|
||||
}
|
||||
|
@ -924,7 +941,7 @@ class S3Client extends AwsClient implements S3ClientInterface
|
|||
|
||||
foreach ($definition['shapes'] as $key => &$value) {
|
||||
$suffix = 'Output';
|
||||
if (substr($key, -strlen($suffix)) === $suffix) {
|
||||
if (str_ends_with($key, $suffix)) {
|
||||
if (isset($value['members']['Expires'])) {
|
||||
$value['members']['Expires']['deprecated'] = true;
|
||||
$value['members']['ExpiresString'] = [
|
||||
|
|
|
@ -20,6 +20,8 @@ use GuzzleHttp\Promise;
|
|||
* @method \GuzzleHttp\Promise\Promise copyObjectAsync(array $args = [])
|
||||
* @method \Aws\Result createBucket(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketAsync(array $args = [])
|
||||
* @method \Aws\Result createBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result createBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise createBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result createMultipartUpload(array $args = [])
|
||||
|
@ -40,6 +42,8 @@ use GuzzleHttp\Promise;
|
|||
* @method \GuzzleHttp\Promise\Promise deleteBucketInventoryConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketLifecycle(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketLifecycleAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise deleteBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result deleteBucketMetricsConfiguration(array $args = [])
|
||||
|
@ -84,6 +88,8 @@ use GuzzleHttp\Promise;
|
|||
* @method \GuzzleHttp\Promise\Promise getBucketLocationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketLogging(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketLoggingAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetadataConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketMetadataConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetadataTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise getBucketMetadataTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result getBucketMetricsConfiguration(array $args = [])
|
||||
|
@ -212,6 +218,10 @@ use GuzzleHttp\Promise;
|
|||
* @method \GuzzleHttp\Promise\Promise restoreObjectAsync(array $args = [])
|
||||
* @method \Aws\Result selectObjectContent(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise selectObjectContentAsync(array $args = [])
|
||||
* @method \Aws\Result updateBucketMetadataInventoryTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBucketMetadataInventoryTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result updateBucketMetadataJournalTableConfiguration(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise updateBucketMetadataJournalTableConfigurationAsync(array $args = [])
|
||||
* @method \Aws\Result uploadPart(array $args = [])
|
||||
* @method \GuzzleHttp\Promise\Promise uploadPartAsync(array $args = [])
|
||||
* @method \Aws\Result uploadPartCopy(array $args = [])
|
||||
|
|
|
@ -259,7 +259,7 @@ class StreamWrapper
|
|||
$this->initProtocol($path);
|
||||
|
||||
// Some paths come through as S3:// for some reason.
|
||||
$split = explode('://', $path);
|
||||
$split = explode('://', $path, 2);
|
||||
$path = strtolower($split[0]) . '://' . $split[1];
|
||||
|
||||
// Check if this path is in the url_stat cache
|
||||
|
@ -703,7 +703,7 @@ class StreamWrapper
|
|||
private function getBucketKey($path)
|
||||
{
|
||||
// Remove the protocol
|
||||
$parts = explode('://', $path);
|
||||
$parts = explode('://', $path, 2);
|
||||
// Get the bucket, key
|
||||
$parts = explode('/', $parts[1], 2);
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionACMPCA(array $args = [])
|
||||
* @method \Aws\AIOps\AIOpsClient createAIOps(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionAIOps(array $args = [])
|
||||
* @method \Aws\ARCRegionSwitch\ARCRegionSwitchClient createARCRegionSwitch(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionARCRegionSwitch(array $args = [])
|
||||
* @method \Aws\ARCZonalShift\ARCZonalShiftClient createARCZonalShift(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionARCZonalShift(array $args = [])
|
||||
* @method \Aws\AccessAnalyzer\AccessAnalyzerClient createAccessAnalyzer(array $args = [])
|
||||
|
@ -74,10 +76,14 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionAutoScalingPlans(array $args = [])
|
||||
* @method \Aws\B2bi\B2biClient createB2bi(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionB2bi(array $args = [])
|
||||
* @method \Aws\BCMDashboards\BCMDashboardsClient createBCMDashboards(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBCMDashboards(array $args = [])
|
||||
* @method \Aws\BCMDataExports\BCMDataExportsClient createBCMDataExports(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBCMDataExports(array $args = [])
|
||||
* @method \Aws\BCMPricingCalculator\BCMPricingCalculatorClient createBCMPricingCalculator(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBCMPricingCalculator(array $args = [])
|
||||
* @method \Aws\BCMRecommendedActions\BCMRecommendedActionsClient createBCMRecommendedActions(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBCMRecommendedActions(array $args = [])
|
||||
* @method \Aws\Backup\BackupClient createBackup(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBackup(array $args = [])
|
||||
* @method \Aws\BackupGateway\BackupGatewayClient createBackupGateway(array $args = [])
|
||||
|
@ -90,6 +96,10 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionBedrock(array $args = [])
|
||||
* @method \Aws\BedrockAgent\BedrockAgentClient createBedrockAgent(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBedrockAgent(array $args = [])
|
||||
* @method \Aws\BedrockAgentCore\BedrockAgentCoreClient createBedrockAgentCore(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBedrockAgentCore(array $args = [])
|
||||
* @method \Aws\BedrockAgentCoreControl\BedrockAgentCoreControlClient createBedrockAgentCoreControl(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBedrockAgentCoreControl(array $args = [])
|
||||
* @method \Aws\BedrockAgentRuntime\BedrockAgentRuntimeClient createBedrockAgentRuntime(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionBedrockAgentRuntime(array $args = [])
|
||||
* @method \Aws\BedrockDataAutomation\BedrockDataAutomationClient createBedrockDataAutomation(array $args = [])
|
||||
|
@ -408,6 +418,8 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionKendraRanking(array $args = [])
|
||||
* @method \Aws\Keyspaces\KeyspacesClient createKeyspaces(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionKeyspaces(array $args = [])
|
||||
* @method \Aws\KeyspacesStreams\KeyspacesStreamsClient createKeyspacesStreams(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionKeyspacesStreams(array $args = [])
|
||||
* @method \Aws\Kinesis\KinesisClient createKinesis(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionKinesis(array $args = [])
|
||||
* @method \Aws\KinesisAnalytics\KinesisAnalyticsClient createKinesisAnalytics(array $args = [])
|
||||
|
@ -548,16 +560,14 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionOSIS(array $args = [])
|
||||
* @method \Aws\ObservabilityAdmin\ObservabilityAdminClient createObservabilityAdmin(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionObservabilityAdmin(array $args = [])
|
||||
* @method \Aws\Odb\OdbClient createOdb(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOdb(array $args = [])
|
||||
* @method \Aws\Omics\OmicsClient createOmics(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOmics(array $args = [])
|
||||
* @method \Aws\OpenSearchServerless\OpenSearchServerlessClient createOpenSearchServerless(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOpenSearchServerless(array $args = [])
|
||||
* @method \Aws\OpenSearchService\OpenSearchServiceClient createOpenSearchService(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOpenSearchService(array $args = [])
|
||||
* @method \Aws\OpsWorks\OpsWorksClient createOpsWorks(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOpsWorks(array $args = [])
|
||||
* @method \Aws\OpsWorksCM\OpsWorksCMClient createOpsWorksCM(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOpsWorksCM(array $args = [])
|
||||
* @method \Aws\Organizations\OrganizationsClient createOrganizations(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionOrganizations(array $args = [])
|
||||
* @method \Aws\Outposts\OutpostsClient createOutposts(array $args = [])
|
||||
|
@ -666,6 +676,8 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionS3Outposts(array $args = [])
|
||||
* @method \Aws\S3Tables\S3TablesClient createS3Tables(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionS3Tables(array $args = [])
|
||||
* @method \Aws\S3Vectors\S3VectorsClient createS3Vectors(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionS3Vectors(array $args = [])
|
||||
* @method \Aws\SSMContacts\SSMContactsClient createSSMContacts(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionSSMContacts(array $args = [])
|
||||
* @method \Aws\SSMGuiConnect\SSMGuiConnectClient createSSMGuiConnect(array $args = [])
|
||||
|
@ -800,6 +812,8 @@ namespace Aws;
|
|||
* @method \Aws\MultiRegionClient createMultiRegionWorkSpacesThinClient(array $args = [])
|
||||
* @method \Aws\WorkSpacesWeb\WorkSpacesWebClient createWorkSpacesWeb(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionWorkSpacesWeb(array $args = [])
|
||||
* @method \Aws\WorkspacesInstances\WorkspacesInstancesClient createWorkspacesInstances(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionWorkspacesInstances(array $args = [])
|
||||
* @method \Aws\XRay\XRayClient createXRay(array $args = [])
|
||||
* @method \Aws\MultiRegionClient createMultiRegionXRay(array $args = [])
|
||||
* @method \Aws\drs\drsClient createdrs(array $args = [])
|
||||
|
@ -819,7 +833,7 @@ namespace Aws;
|
|||
*/
|
||||
class Sdk
|
||||
{
|
||||
const VERSION = '3.346.2';
|
||||
const VERSION = '3.356.8';
|
||||
|
||||
/** @var array Arguments for creating clients */
|
||||
private $args;
|
||||
|
|
|
@ -73,7 +73,7 @@ class StreamRequestPayloadMiddleware
|
|||
. ' calculated.');
|
||||
}
|
||||
$request = $request->withHeader(
|
||||
'content-length',
|
||||
'Content-Length',
|
||||
$size
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ use GuzzleHttp\Promise\PromiseInterface;
|
|||
class ConfigurationProvider extends AbstractConfigurationProvider
|
||||
implements ConfigurationProviderInterface
|
||||
{
|
||||
const DEFAULT_ENDPOINTS_TYPE = 'legacy';
|
||||
const DEFAULT_ENDPOINTS_TYPE = 'regional';
|
||||
const ENV_ENDPOINTS_TYPE = 'AWS_STS_REGIONAL_ENDPOINTS';
|
||||
const ENV_PROFILE = 'AWS_PROFILE';
|
||||
const INI_ENDPOINTS_TYPE = 'sts_regional_endpoints';
|
||||
|
|
102
lam/lib/3rdParty/composer/aws/aws-sdk-php/src/Token/BedrockTokenProvider.php
vendored
Normal file
102
lam/lib/3rdParty/composer/aws/aws-sdk-php/src/Token/BedrockTokenProvider.php
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Aws\Token;
|
||||
|
||||
use Aws\Configuration\ConfigurationResolver;
|
||||
use Aws\Exception\TokenException;
|
||||
use GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* Token provider for Bedrock that sources bearer tokens from environment variables.
|
||||
*/
|
||||
class BedrockTokenProvider extends TokenProvider
|
||||
{
|
||||
/** @var string used to resolve the AWS_BEARER_TOKEN_BEDROCK env var */
|
||||
public const TOKEN_ENV_KEY = 'bearer_token_bedrock';
|
||||
public const BEARER_AUTH = 'smithy.api#httpBearerAuth';
|
||||
|
||||
/**
|
||||
* Create a default Bedrock token provider that checks for a bearer token
|
||||
* in the AWS_BEARER_TOKEN_BEDROCK environment variable.
|
||||
*
|
||||
* This provider is automatically wrapped in a memoize function that caches
|
||||
* previously provided tokens.
|
||||
*
|
||||
* @param array $config Optional array of token provider options.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function defaultProvider(array $config = []): callable
|
||||
{
|
||||
$defaultChain = ['env' => self::env(self::TOKEN_ENV_KEY)];
|
||||
|
||||
return self::memoize(
|
||||
call_user_func_array(
|
||||
[TokenProvider::class, 'chain'],
|
||||
array_values($defaultChain)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Token provider that creates a token from an environment variable.
|
||||
*
|
||||
* @param string $configKey The configuration key that will be transformed
|
||||
* to an environment variable name by ConfigurationResolver
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function env(string $configKey): callable
|
||||
{
|
||||
return static function () use ($configKey) {
|
||||
$tokenValue = ConfigurationResolver::env($configKey);
|
||||
if (empty($tokenValue)) {
|
||||
return Promise\Create::rejectionFor(
|
||||
new TokenException(
|
||||
"No token found in environment variable " .
|
||||
ConfigurationResolver::$envPrefix . strtoupper($configKey)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise\Create::promiseFor(new Token($tokenValue));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token provider from a raw token value string.
|
||||
* Bedrock bearer tokens sourced from env do not have an expiration
|
||||
*
|
||||
* @param string $tokenValue The bearer token value
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function fromTokenValue(string $tokenValue): callable
|
||||
{
|
||||
$token = new Token($tokenValue);
|
||||
return self::fromToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Bedrock token provider if the service is 'bedrock' and a token is available.
|
||||
* Sets auth scheme preference to `bearer` auth.
|
||||
*
|
||||
* @param array $args Configuration arguments containing 'config' array
|
||||
*
|
||||
* @return callable|null Returns a token provider if conditions are met, null otherwise
|
||||
*/
|
||||
public static function createIfAvailable(array &$args): ?callable
|
||||
{
|
||||
$tokenValue = ConfigurationResolver::env(self::TOKEN_ENV_KEY);
|
||||
|
||||
if ($tokenValue) {
|
||||
$authSchemePreference = $args['config']['auth_scheme_preference'] ?? [];
|
||||
array_unshift($authSchemePreference, self::BEARER_AUTH);
|
||||
$args['config']['auth_scheme_preference'] = $authSchemePreference;
|
||||
|
||||
return self::fromTokenValue($tokenValue);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
namespace Aws\Token;
|
||||
|
||||
use Aws\Identity\BearerTokenIdentity;
|
||||
use Aws\Token\TokenInterface;
|
||||
|
||||
/**
|
||||
* Basic implementation of the AWS Token interface that allows callers to
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
namespace Aws\Token;
|
||||
|
||||
use Aws;
|
||||
use Aws\Api\DateTimeResult;
|
||||
use Aws\CacheInterface;
|
||||
use Aws\Exception\TokenException;
|
||||
use GuzzleHttp\Promise;
|
||||
|
@ -28,9 +27,10 @@ use GuzzleHttp\Promise;
|
|||
*/
|
||||
class TokenProvider
|
||||
{
|
||||
use ParsesIniTrait;
|
||||
const ENV_PROFILE = 'AWS_PROFILE';
|
||||
|
||||
use ParsesIniTrait;
|
||||
|
||||
/**
|
||||
* Create a default token provider tha checks for cached a SSO token from
|
||||
* the CLI
|
||||
|
@ -44,15 +44,13 @@ class TokenProvider
|
|||
*/
|
||||
public static function defaultProvider(array $config = [])
|
||||
{
|
||||
|
||||
$cacheable = [
|
||||
'sso',
|
||||
];
|
||||
|
||||
$defaultChain = [];
|
||||
|
||||
if (
|
||||
!isset($config['use_aws_shared_config_files'])
|
||||
if (!isset($config['use_aws_shared_config_files'])
|
||||
|| $config['use_aws_shared_config_files'] !== false
|
||||
) {
|
||||
$profileName = getenv(self::ENV_PROFILE) ?: 'default';
|
||||
|
@ -79,7 +77,7 @@ class TokenProvider
|
|||
|
||||
return self::memoize(
|
||||
call_user_func_array(
|
||||
[TokenProvider::class, 'chain'],
|
||||
[__CLASS__, 'chain'],
|
||||
array_values($defaultChain)
|
||||
)
|
||||
);
|
||||
|
@ -96,7 +94,7 @@ class TokenProvider
|
|||
{
|
||||
$promise = Promise\Create::promiseFor($token);
|
||||
|
||||
return function () use ($promise) {
|
||||
return static function () use ($promise) {
|
||||
return $promise;
|
||||
};
|
||||
}
|
||||
|
@ -113,12 +111,12 @@ class TokenProvider
|
|||
$links = func_get_args();
|
||||
//Common use case for when aws_shared_config_files is false
|
||||
if (empty($links)) {
|
||||
return function () {
|
||||
return static function () {
|
||||
return Promise\Create::promiseFor(false);
|
||||
};
|
||||
}
|
||||
|
||||
return function () use ($links) {
|
||||
return static function () use ($links) {
|
||||
/** @var callable $parent */
|
||||
$parent = array_shift($links);
|
||||
$promise = $parent();
|
||||
|
@ -138,7 +136,7 @@ class TokenProvider
|
|||
*/
|
||||
public static function memoize(callable $provider)
|
||||
{
|
||||
return function () use ($provider) {
|
||||
return static function () use ($provider) {
|
||||
static $result;
|
||||
static $isConstant;
|
||||
|
||||
|
@ -190,10 +188,10 @@ class TokenProvider
|
|||
callable $provider,
|
||||
CacheInterface $cache,
|
||||
$cacheKey = null
|
||||
) {
|
||||
){
|
||||
$cacheKey = $cacheKey ?: 'aws_cached_token';
|
||||
|
||||
return function () use ($provider, $cache, $cacheKey) {
|
||||
return static function () use ($provider, $cache, $cacheKey) {
|
||||
$found = $cache->get($cacheKey);
|
||||
if (is_array($found) && isset($found['token'])) {
|
||||
$foundToken = $found['token'];
|
||||
|
@ -214,7 +212,7 @@ class TokenProvider
|
|||
) {
|
||||
$cache->set(
|
||||
$cacheKey,
|
||||
$token,
|
||||
['token' => $token],
|
||||
null === $token->getExpiration() ?
|
||||
0 : $token->getExpiration() - time()
|
||||
);
|
||||
|
@ -227,7 +225,8 @@ class TokenProvider
|
|||
/**
|
||||
* Gets profiles from the ~/.aws/config ini file
|
||||
*/
|
||||
private static function loadDefaultProfiles() {
|
||||
private static function loadDefaultProfiles()
|
||||
{
|
||||
$profiles = [];
|
||||
$configFile = self::getHomeDir() . '/.aws/config';
|
||||
|
||||
|
@ -260,11 +259,13 @@ class TokenProvider
|
|||
* @return SsoTokenProvider
|
||||
* @see Aws\Token\SsoTokenProvider for $config details.
|
||||
*/
|
||||
public static function sso($profileName, $filename, $config = [])
|
||||
{
|
||||
$ssoClient = isset($config['ssoClient']) ? $config['ssoClient'] : null;
|
||||
public static function sso(
|
||||
$profileName,
|
||||
$filename,
|
||||
$config = []
|
||||
){
|
||||
$ssoClient = $config['ssoClient'] ?? null;
|
||||
|
||||
return new SsoTokenProvider($profileName, $filename, $ssoClient);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
<?php
|
||||
// This file was auto-generated from sdk-root/src/data/grandfathered-services.json
|
||||
return [ 'grandfathered-services' => [ 'AccessAnalyzer', 'Account', 'ACMPCA', 'ACM', 'PrometheusService', 'Amplify', 'AmplifyBackend', 'AmplifyUIBuilder', 'APIGateway', 'ApiGatewayManagementApi', 'ApiGatewayV2', 'AppConfig', 'AppConfigData', 'Appflow', 'AppIntegrationsService', 'ApplicationAutoScaling', 'ApplicationInsights', 'ApplicationCostProfiler', 'AppMesh', 'AppRunner', 'AppStream', 'AppSync', 'Athena', 'AuditManager', 'AutoScalingPlans', 'AutoScaling', 'BackupGateway', 'Backup', 'Batch', 'BillingConductor', 'Braket', 'Budgets', 'CostExplorer', 'ChimeSDKIdentity', 'ChimeSDKMediaPipelines', 'ChimeSDKMeetings', 'ChimeSDKMessaging', 'Chime', 'Cloud9', 'CloudControlApi', 'CloudDirectory', 'CloudFormation', 'CloudFront', 'CloudHSM', 'CloudHSMV2', 'CloudSearch', 'CloudSearchDomain', 'CloudTrail', 'CodeArtifact', 'CodeBuild', 'CodeCommit', 'CodeDeploy', 'CodeGuruReviewer', 'CodeGuruProfiler', 'CodePipeline', 'CodeStarconnections', 'CodeStarNotifications', 'CodeStar', 'CognitoIdentity', 'CognitoIdentityProvider', 'CognitoSync', 'Comprehend', 'ComprehendMedical', 'ComputeOptimizer', 'ConfigService', 'ConnectContactLens', 'Connect', 'ConnectCampaignService', 'ConnectParticipant', 'CostandUsageReportService', 'CustomerProfiles', 'IoTDataPlane', 'GlueDataBrew', 'DataExchange', 'DataPipeline', 'DataSync', 'DAX', 'Detective', 'DeviceFarm', 'DevOpsGuru', 'DirectConnect', 'ApplicationDiscoveryService', 'DLM', 'DatabaseMigrationService', 'DocDB', 'drs', 'DirectoryService', 'DynamoDB', 'EBS', 'EC2InstanceConnect', 'EC2', 'ECRPublic', 'ECR', 'ECS', 'EKS', 'ElastiCache', 'ElasticBeanstalk', 'EFS', 'ElasticLoadBalancing', 'ElasticLoadBalancingv2', 'EMR', 'ElasticTranscoder', 'SES', 'EMRContainers', 'EMRServerless', 'MarketplaceEntitlementService', 'ElasticsearchService', 'EventBridge', 'CloudWatchEvents', 'CloudWatchEvidently', 'FinSpaceData', 'finspace', 'Firehose', 'FIS', 'FMS', 'ForecastService', 'ForecastQueryService', 'FraudDetector', 'FSx', 'GameLift', 'Glacier', 'GlobalAccelerator', 'Glue', 'ManagedGrafana', 'Greengrass', 'GreengrassV2', 'GroundStation', 'GuardDuty', 'Health', 'HealthLake', 'IAM', 'IdentityStore', 'imagebuilder', 'ImportExport', 'Inspector', 'Inspector2', 'IoTJobsDataPlane', 'IoT', 'IoTAnalytics', 'IoTDeviceAdvisor', 'IoTEventsData', 'IoTEvents', 'IoTFleetHub', 'IoTSecureTunneling', 'IoTSiteWise', 'IoTThingsGraph', 'IoTTwinMaker', 'IoTWireless', 'IVS', 'ivschat', 'Kafka', 'KafkaConnect', 'kendra', 'Keyspaces', 'KinesisVideoArchivedMedia', 'KinesisVideoMedia', 'KinesisVideoSignalingChannels', 'Kinesis', 'KinesisAnalytics', 'KinesisAnalyticsV2', 'KinesisVideo', 'KMS', 'LakeFormation', 'Lambda', 'LexModelBuildingService', 'LicenseManager', 'Lightsail', 'LocationService', 'CloudWatchLogs', 'LookoutEquipment', 'LookoutMetrics', 'LookoutforVision', 'MainframeModernization', 'MachineLearning', 'Macie2', 'ManagedBlockchain', 'MarketplaceCatalog', 'MarketplaceCommerceAnalytics', 'MediaConnect', 'MediaConvert', 'MediaLive', 'MediaPackageVod', 'MediaPackage', 'MediaStoreData', 'MediaStore', 'MediaTailor', 'MemoryDB', 'MarketplaceMetering', 'MigrationHub', 'mgn', 'MigrationHubRefactorSpaces', 'MigrationHubConfig', 'MigrationHubStrategyRecommendations', 'LexModelsV2', 'CloudWatch', 'MQ', 'MTurk', 'MWAA', 'Neptune', 'NetworkFirewall', 'NetworkManager', 'OpenSearchService', 'OpsWorks', 'OpsWorksCM', 'Organizations', 'Outposts', 'Panorama', 'PersonalizeEvents', 'PersonalizeRuntime', 'Personalize', 'PI', 'PinpointEmail', 'PinpointSMSVoiceV2', 'Pinpoint', 'Polly', 'Pricing', 'Proton', 'QLDBSession', 'QLDB', 'QuickSight', 'RAM', 'RecycleBin', 'RDSDataService', 'RDS', 'RedshiftDataAPIService', 'RedshiftServerless', 'Redshift', 'Rekognition', 'ResilienceHub', 'ResourceGroups', 'ResourceGroupsTaggingAPI', 'RoboMaker', 'Route53RecoveryCluster', 'Route53RecoveryControlConfig', 'Route53RecoveryReadiness', 'Route53', 'Route53Domains', 'Route53Resolver', 'CloudWatchRUM', 'LexRuntimeV2', 'LexRuntimeService', 'SageMakerRuntime', 'S3', 'S3Control', 'S3Outposts', 'AugmentedAIRuntime', 'SagemakerEdgeManager', 'SageMakerFeatureStoreRuntime', 'SageMaker', 'SavingsPlans', 'Schemas', 'SecretsManager', 'SecurityHub', 'ServerlessApplicationRepository', 'ServiceQuotas', 'AppRegistry', 'ServiceCatalog', 'ServiceDiscovery', 'SESV2', 'Shield', 'signer', 'PinpointSMSVoice', 'SMS', 'SnowDeviceManagement', 'Snowball', 'SNS', 'SQS', 'SSMContacts', 'SSMIncidents', 'SSM', 'SSOAdmin', 'SSOOIDC', 'SSO', 'SFN', 'StorageGateway', 'DynamoDBStreams', 'STS', 'Support', 'SWF', 'Synthetics', 'Textract', 'TimestreamQuery', 'TimestreamWrite', 'TranscribeService', 'Transfer', 'Translate', 'VoiceID', 'WAFRegional', 'WAF', 'WAFV2', 'WellArchitected', 'ConnectWisdomService', 'WorkDocs', 'WorkMail', 'WorkMailMessageFlow', 'WorkSpacesWeb', 'WorkSpaces', 'XRay', ],];
|
||||
return [ 'grandfathered-services' => [ 'AccessAnalyzer', 'Account', 'ACMPCA', 'ACM', 'PrometheusService', 'Amplify', 'AmplifyBackend', 'AmplifyUIBuilder', 'APIGateway', 'ApiGatewayManagementApi', 'ApiGatewayV2', 'AppConfig', 'AppConfigData', 'Appflow', 'AppIntegrationsService', 'ApplicationAutoScaling', 'ApplicationInsights', 'ApplicationCostProfiler', 'AppMesh', 'AppRunner', 'AppStream', 'AppSync', 'Athena', 'AuditManager', 'AutoScalingPlans', 'AutoScaling', 'BackupGateway', 'Backup', 'Batch', 'BillingConductor', 'Braket', 'Budgets', 'CostExplorer', 'ChimeSDKIdentity', 'ChimeSDKMediaPipelines', 'ChimeSDKMeetings', 'ChimeSDKMessaging', 'Chime', 'Cloud9', 'CloudControlApi', 'CloudDirectory', 'CloudFormation', 'CloudFront', 'CloudHSM', 'CloudHSMV2', 'CloudSearch', 'CloudSearchDomain', 'CloudTrail', 'CodeArtifact', 'CodeBuild', 'CodeCommit', 'CodeDeploy', 'CodeGuruReviewer', 'CodeGuruProfiler', 'CodePipeline', 'CodeStarconnections', 'CodeStarNotifications', 'CodeStar', 'CognitoIdentity', 'CognitoIdentityProvider', 'CognitoSync', 'Comprehend', 'ComprehendMedical', 'ComputeOptimizer', 'ConfigService', 'ConnectContactLens', 'Connect', 'ConnectCampaignService', 'ConnectParticipant', 'CostandUsageReportService', 'CustomerProfiles', 'IoTDataPlane', 'GlueDataBrew', 'DataExchange', 'DataPipeline', 'DataSync', 'DAX', 'Detective', 'DeviceFarm', 'DevOpsGuru', 'DirectConnect', 'ApplicationDiscoveryService', 'DLM', 'DatabaseMigrationService', 'DocDB', 'drs', 'DirectoryService', 'DynamoDB', 'EBS', 'EC2InstanceConnect', 'EC2', 'ECRPublic', 'ECR', 'ECS', 'EKS', 'ElastiCache', 'ElasticBeanstalk', 'EFS', 'ElasticLoadBalancing', 'ElasticLoadBalancingv2', 'EMR', 'ElasticTranscoder', 'SES', 'EMRContainers', 'EMRServerless', 'MarketplaceEntitlementService', 'ElasticsearchService', 'EventBridge', 'CloudWatchEvents', 'CloudWatchEvidently', 'FinSpaceData', 'finspace', 'Firehose', 'FIS', 'FMS', 'ForecastService', 'ForecastQueryService', 'FraudDetector', 'FSx', 'GameLift', 'Glacier', 'GlobalAccelerator', 'Glue', 'ManagedGrafana', 'Greengrass', 'GreengrassV2', 'GroundStation', 'GuardDuty', 'Health', 'HealthLake', 'IAM', 'IdentityStore', 'imagebuilder', 'ImportExport', 'Inspector', 'Inspector2', 'IoTJobsDataPlane', 'IoT', 'IoTAnalytics', 'IoTDeviceAdvisor', 'IoTEventsData', 'IoTEvents', 'IoTFleetHub', 'IoTSecureTunneling', 'IoTSiteWise', 'IoTThingsGraph', 'IoTTwinMaker', 'IoTWireless', 'IVS', 'ivschat', 'Kafka', 'KafkaConnect', 'kendra', 'Keyspaces', 'KinesisVideoArchivedMedia', 'KinesisVideoMedia', 'KinesisVideoSignalingChannels', 'Kinesis', 'KinesisAnalytics', 'KinesisAnalyticsV2', 'KinesisVideo', 'KMS', 'LakeFormation', 'Lambda', 'LexModelBuildingService', 'LicenseManager', 'Lightsail', 'LocationService', 'CloudWatchLogs', 'LookoutEquipment', 'LookoutMetrics', 'LookoutforVision', 'MainframeModernization', 'MachineLearning', 'Macie2', 'ManagedBlockchain', 'MarketplaceCatalog', 'MarketplaceCommerceAnalytics', 'MediaConnect', 'MediaConvert', 'MediaLive', 'MediaPackageVod', 'MediaPackage', 'MediaStoreData', 'MediaStore', 'MediaTailor', 'MemoryDB', 'MarketplaceMetering', 'MigrationHub', 'mgn', 'MigrationHubRefactorSpaces', 'MigrationHubConfig', 'MigrationHubStrategyRecommendations', 'LexModelsV2', 'CloudWatch', 'MQ', 'MTurk', 'MWAA', 'Neptune', 'NetworkFirewall', 'NetworkManager', 'OpenSearchService', 'Organizations', 'Outposts', 'Panorama', 'PersonalizeEvents', 'PersonalizeRuntime', 'Personalize', 'PI', 'PinpointEmail', 'PinpointSMSVoiceV2', 'Pinpoint', 'Polly', 'Pricing', 'Proton', 'QLDBSession', 'QLDB', 'QuickSight', 'RAM', 'RecycleBin', 'RDSDataService', 'RDS', 'RedshiftDataAPIService', 'RedshiftServerless', 'Redshift', 'Rekognition', 'ResilienceHub', 'ResourceGroups', 'ResourceGroupsTaggingAPI', 'RoboMaker', 'Route53RecoveryCluster', 'Route53RecoveryControlConfig', 'Route53RecoveryReadiness', 'Route53', 'Route53Domains', 'Route53Resolver', 'CloudWatchRUM', 'LexRuntimeV2', 'LexRuntimeService', 'SageMakerRuntime', 'S3', 'S3Control', 'S3Outposts', 'AugmentedAIRuntime', 'SagemakerEdgeManager', 'SageMakerFeatureStoreRuntime', 'SageMaker', 'SavingsPlans', 'Schemas', 'SecretsManager', 'SecurityHub', 'ServerlessApplicationRepository', 'ServiceQuotas', 'AppRegistry', 'ServiceCatalog', 'ServiceDiscovery', 'SESV2', 'Shield', 'signer', 'PinpointSMSVoice', 'SMS', 'SnowDeviceManagement', 'Snowball', 'SNS', 'SQS', 'SSMContacts', 'SSMIncidents', 'SSM', 'SSOAdmin', 'SSOOIDC', 'SSO', 'SFN', 'StorageGateway', 'DynamoDBStreams', 'STS', 'Support', 'SWF', 'Synthetics', 'Textract', 'TimestreamQuery', 'TimestreamWrite', 'TranscribeService', 'Transfer', 'Translate', 'VoiceID', 'WAFRegional', 'WAF', 'WAFV2', 'WellArchitected', 'ConnectWisdomService', 'WorkDocs', 'WorkMail', 'WorkMailMessageFlow', 'WorkSpacesWeb', 'WorkSpaces', 'XRay', ],];
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
<?php
|
||||
// This file was auto-generated from sdk-root/src/data/sso/2019-06-10/endpoint-rule-set-1.json
|
||||
return [ 'version' => '1.0', 'parameters' => [ 'Region' => [ 'builtIn' => 'AWS::Region', 'required' => false, 'documentation' => 'The AWS region used to dispatch the request.', 'type' => 'String', ], 'UseDualStack' => [ 'builtIn' => 'AWS::UseDualStack', 'required' => true, 'default' => false, 'documentation' => 'When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.', 'type' => 'Boolean', ], 'UseFIPS' => [ 'builtIn' => 'AWS::UseFIPS', 'required' => true, 'default' => false, 'documentation' => 'When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.', 'type' => 'Boolean', ], 'Endpoint' => [ 'builtIn' => 'SDK::Endpoint', 'required' => false, 'documentation' => 'Override the endpoint used to send this request', 'type' => 'String', ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'isSet', 'argv' => [ [ 'ref' => 'Endpoint', ], ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], ], 'error' => 'Invalid Configuration: FIPS and custom endpoint are not supported', 'type' => 'error', ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'error' => 'Invalid Configuration: Dualstack and custom endpoint are not supported', 'type' => 'error', ], [ 'conditions' => [], 'endpoint' => [ 'url' => [ 'ref' => 'Endpoint', ], 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], ], [ 'conditions' => [ [ 'fn' => 'isSet', 'argv' => [ [ 'ref' => 'Region', ], ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'aws.partition', 'argv' => [ [ 'ref' => 'Region', ], ], 'assign' => 'PartitionResult', ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsFIPS', ], ], ], ], [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsDualStack', ], ], ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso-fips.{Region}.{PartitionResult#dualStackDnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], ], [ 'conditions' => [], 'error' => 'FIPS and DualStack are enabled, but this partition does not support one or both', 'type' => 'error', ], ], ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsFIPS', ], ], ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'stringEquals', 'argv' => [ 'aws-us-gov', [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'name', ], ], ], ], ], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.amazonaws.com', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso-fips.{Region}.{PartitionResult#dnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], ], [ 'conditions' => [], 'error' => 'FIPS is enabled but this partition does not support FIPS', 'type' => 'error', ], ], ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsDualStack', ], ], ], ], ], 'type' => 'tree', 'rules' => [ [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.{PartitionResult#dualStackDnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], ], [ 'conditions' => [], 'error' => 'DualStack is enabled but this partition does not support DualStack', 'type' => 'error', ], ], ], [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.{PartitionResult#dnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], ], ], ], [ 'conditions' => [], 'error' => 'Invalid Configuration: Missing Region', 'type' => 'error', ], ],];
|
||||
return [ 'version' => '1.0', 'parameters' => [ 'Region' => [ 'builtIn' => 'AWS::Region', 'required' => false, 'documentation' => 'The AWS region used to dispatch the request.', 'type' => 'String', ], 'UseDualStack' => [ 'builtIn' => 'AWS::UseDualStack', 'required' => true, 'default' => false, 'documentation' => 'When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.', 'type' => 'Boolean', ], 'UseFIPS' => [ 'builtIn' => 'AWS::UseFIPS', 'required' => true, 'default' => false, 'documentation' => 'When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.', 'type' => 'Boolean', ], 'Endpoint' => [ 'builtIn' => 'SDK::Endpoint', 'required' => false, 'documentation' => 'Override the endpoint used to send this request', 'type' => 'String', ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'isSet', 'argv' => [ [ 'ref' => 'Endpoint', ], ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], ], 'error' => 'Invalid Configuration: FIPS and custom endpoint are not supported', 'type' => 'error', ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'error' => 'Invalid Configuration: Dualstack and custom endpoint are not supported', 'type' => 'error', ], [ 'conditions' => [], 'endpoint' => [ 'url' => [ 'ref' => 'Endpoint', ], 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], 'type' => 'tree', ], [ 'conditions' => [ [ 'fn' => 'isSet', 'argv' => [ [ 'ref' => 'Region', ], ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'aws.partition', 'argv' => [ [ 'ref' => 'Region', ], ], 'assign' => 'PartitionResult', ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsFIPS', ], ], ], ], [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsDualStack', ], ], ], ], ], 'rules' => [ [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso-fips.{Region}.{PartitionResult#dualStackDnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], 'type' => 'tree', ], [ 'conditions' => [], 'error' => 'FIPS and DualStack are enabled, but this partition does not support one or both', 'type' => 'error', ], ], 'type' => 'tree', ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseFIPS', ], true, ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsFIPS', ], ], true, ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'stringEquals', 'argv' => [ [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'name', ], ], 'aws-us-gov', ], ], ], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.amazonaws.com', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso-fips.{Region}.{PartitionResult#dnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], 'type' => 'tree', ], [ 'conditions' => [], 'error' => 'FIPS is enabled but this partition does not support FIPS', 'type' => 'error', ], ], 'type' => 'tree', ], [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ [ 'ref' => 'UseDualStack', ], true, ], ], ], 'rules' => [ [ 'conditions' => [ [ 'fn' => 'booleanEquals', 'argv' => [ true, [ 'fn' => 'getAttr', 'argv' => [ [ 'ref' => 'PartitionResult', ], 'supportsDualStack', ], ], ], ], ], 'rules' => [ [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.{PartitionResult#dualStackDnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], 'type' => 'tree', ], [ 'conditions' => [], 'error' => 'DualStack is enabled but this partition does not support DualStack', 'type' => 'error', ], ], 'type' => 'tree', ], [ 'conditions' => [], 'endpoint' => [ 'url' => 'https://portal.sso.{Region}.{PartitionResult#dnsSuffix}', 'properties' => [], 'headers' => [], ], 'type' => 'endpoint', ], ], 'type' => 'tree', ], ], 'type' => 'tree', ], [ 'conditions' => [], 'error' => 'Invalid Configuration: Missing Region', 'type' => 'error', ], ],];
|
||||
|
|
|
@ -8,12 +8,12 @@ $baseDir = dirname(dirname(dirname($vendorDir)));
|
|||
return array(
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||
'72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
|
||||
|
|
|
@ -9,12 +9,12 @@ class ComposerStaticInited73ceb9c1bdec18b7c6d09764d1bce5
|
|||
public static $files = array (
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
||||
'72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
|
||||
|
|
508
lam/lib/3rdParty/composer/composer/installed.json
vendored
508
lam/lib/3rdParty/composer/composer/installed.json
vendored
File diff suppressed because it is too large
Load diff
178
lam/lib/3rdParty/composer/composer/installed.php
vendored
178
lam/lib/3rdParty/composer/composer/installed.php
vendored
|
@ -1,8 +1,8 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'ldap-account-manager/ldap-account-manager',
|
||||
'pretty_version' => '9.2',
|
||||
'version' => '9.2.0.0',
|
||||
'pretty_version' => '9.3',
|
||||
'version' => '9.3.0.0',
|
||||
'reference' => null,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../../',
|
||||
|
@ -20,9 +20,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'aws/aws-sdk-php' => array(
|
||||
'pretty_version' => '3.346.2',
|
||||
'version' => '3.346.2.0',
|
||||
'reference' => 'd1403b5a39af7ab7af4fc538deb33013c19c8d33',
|
||||
'pretty_version' => '3.356.8',
|
||||
'version' => '3.356.8.0',
|
||||
'reference' => '3efa8c62c11fedb17b90f60b2d3a9f815b406e63',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../aws/aws-sdk-php',
|
||||
'aliases' => array(),
|
||||
|
@ -65,9 +65,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'duosecurity/duo_universal_php' => array(
|
||||
'pretty_version' => '1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'a2852c46949a2de9ca6da908e4353a81c61b43a3',
|
||||
'pretty_version' => '1.1.1',
|
||||
'version' => '1.1.1.0',
|
||||
'reference' => '4ee7253863d84653a60a8cad4b03aa3b66fcfd35',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../duosecurity/duo_universal_php',
|
||||
'aliases' => array(),
|
||||
|
@ -101,27 +101,27 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/guzzle' => array(
|
||||
'pretty_version' => '7.9.3',
|
||||
'version' => '7.9.3.0',
|
||||
'reference' => '7b2f29fe81dc4da0ca0ea7d42107a0845946ea77',
|
||||
'pretty_version' => '7.10.0',
|
||||
'version' => '7.10.0.0',
|
||||
'reference' => 'b51ac707cfa420b7bfd4e4d5e510ba8008e822b4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/promises' => array(
|
||||
'pretty_version' => '2.2.0',
|
||||
'version' => '2.2.0.0',
|
||||
'reference' => '7c69f28996b0a6920945dd20b3857e499d9ca96c',
|
||||
'pretty_version' => '2.3.0',
|
||||
'version' => '2.3.0.0',
|
||||
'reference' => '481557b130ef3790cf82b713667b43030dc9c957',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/psr7' => array(
|
||||
'pretty_version' => '2.7.1',
|
||||
'version' => '2.7.1.0',
|
||||
'reference' => 'c2270caaabe631b3b44c85f99e5a04bbb8060d16',
|
||||
'pretty_version' => '2.8.0',
|
||||
'version' => '2.8.0.0',
|
||||
'reference' => '21dc724a0583619cd1652f673303492272778051',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||
'aliases' => array(),
|
||||
|
@ -173,8 +173,8 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'ldap-account-manager/ldap-account-manager' => array(
|
||||
'pretty_version' => '9.2',
|
||||
'version' => '9.2.0.0',
|
||||
'pretty_version' => '9.3',
|
||||
'version' => '9.3.0.0',
|
||||
'reference' => null,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../../',
|
||||
|
@ -200,9 +200,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'nesbot/carbon' => array(
|
||||
'pretty_version' => '3.9.1',
|
||||
'version' => '3.9.1.0',
|
||||
'reference' => 'ced71f79398ece168e24f7f7710462f462310d4d',
|
||||
'pretty_version' => '3.10.2',
|
||||
'version' => '3.10.2.0',
|
||||
'reference' => '76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nesbot/carbon',
|
||||
'aliases' => array(),
|
||||
|
@ -266,9 +266,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'phpseclib/phpseclib' => array(
|
||||
'pretty_version' => '3.0.43',
|
||||
'version' => '3.0.43.0',
|
||||
'reference' => '709ec107af3cb2f385b9617be72af8cf62441d02',
|
||||
'pretty_version' => '3.0.46',
|
||||
'version' => '3.0.46.0',
|
||||
'reference' => '56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
|
||||
'aliases' => array(),
|
||||
|
@ -344,9 +344,9 @@
|
|||
'psr/http-factory-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
1 => '^1.0',
|
||||
2 => '*',
|
||||
0 => '^1.0',
|
||||
1 => '*',
|
||||
2 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
|
@ -361,8 +361,8 @@
|
|||
'psr/http-message-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
1 => '*',
|
||||
0 => '*',
|
||||
1 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-server-handler' => array(
|
||||
|
@ -436,63 +436,63 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'spomky-labs/cbor-php' => array(
|
||||
'pretty_version' => '3.1.0',
|
||||
'version' => '3.1.0.0',
|
||||
'reference' => '499d9bff0a6d59c4f1b813cc617fc3fd56d6dca4',
|
||||
'pretty_version' => '3.1.1',
|
||||
'version' => '3.1.1.0',
|
||||
'reference' => '5404f3e21cbe72f5cf612aa23db2b922fd2f43bf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../spomky-labs/cbor-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'spomky-labs/pki-framework' => array(
|
||||
'pretty_version' => '1.2.3',
|
||||
'version' => '1.2.3.0',
|
||||
'reference' => '5ff1dcc21e961b60149a80e77f744fc047800b31',
|
||||
'pretty_version' => '1.3.0',
|
||||
'version' => '1.3.0.0',
|
||||
'reference' => 'eced5b5ce70518b983ff2be486e902bbd15135ae',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../spomky-labs/pki-framework',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/clock' => array(
|
||||
'pretty_version' => 'v6.4.13',
|
||||
'version' => '6.4.13.0',
|
||||
'reference' => 'b2bf55c4dd115003309eafa87ee7df9ed3dde81b',
|
||||
'pretty_version' => 'v6.4.24',
|
||||
'version' => '6.4.24.0',
|
||||
'reference' => '5e15a9c9aeeb44a99f7cf24aa75aa9607795f6f8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/clock',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/console' => array(
|
||||
'pretty_version' => 'v6.4.21',
|
||||
'version' => '6.4.21.0',
|
||||
'reference' => 'a3011c7b7adb58d89f6c0d822abb641d7a5f9719',
|
||||
'pretty_version' => 'v6.4.25',
|
||||
'version' => '6.4.25.0',
|
||||
'reference' => '273fd29ff30ba0a88ca5fb83f7cf1ab69306adae',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/console',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v3.5.1',
|
||||
'version' => '3.5.1.0',
|
||||
'reference' => '74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6',
|
||||
'pretty_version' => 'v3.6.0',
|
||||
'version' => '3.6.0.0',
|
||||
'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/http-client' => array(
|
||||
'pretty_version' => 'v6.4.19',
|
||||
'version' => '6.4.19.0',
|
||||
'reference' => '3294a433fc9d12ae58128174896b5b1822c28dad',
|
||||
'pretty_version' => 'v6.4.25',
|
||||
'version' => '6.4.25.0',
|
||||
'reference' => 'b8e9dce2d8acba3c32af467bb58e0c3656886181',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/http-client',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/http-client-contracts' => array(
|
||||
'pretty_version' => 'v3.5.2',
|
||||
'version' => '3.5.2.0',
|
||||
'reference' => 'ee8d807ab20fcb51267fdace50fbe3494c31e645',
|
||||
'pretty_version' => 'v3.6.0',
|
||||
'version' => '3.6.0.0',
|
||||
'reference' => '75d7043853a42837e68111812f4d964b01e5101c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/http-client-contracts',
|
||||
'aliases' => array(),
|
||||
|
@ -505,17 +505,17 @@
|
|||
),
|
||||
),
|
||||
'symfony/http-foundation' => array(
|
||||
'pretty_version' => 'v6.4.21',
|
||||
'version' => '6.4.21.0',
|
||||
'reference' => '3f0c7ea41db479383b81d436b836d37168fd5b99',
|
||||
'pretty_version' => 'v6.4.25',
|
||||
'version' => '6.4.25.0',
|
||||
'reference' => '6bc974c0035b643aa497c58d46d9e25185e4b272',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/http-foundation',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-ctype' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||
|
@ -523,17 +523,17 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-grapheme' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'reference' => 'b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '380872130d3a5dd3ace2f4010d95125fde5d5c70',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-normalizer' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '3833d7255cc303546435cb650316bff708a1c75c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||
|
@ -541,8 +541,8 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
|
@ -550,17 +550,17 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php83' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'reference' => '2fb86d65e2d424369ad2905e83b236a8805ba491',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '17f6f9a6b1735c0f163024d959f700cfbc5155e5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-uuid' => array(
|
||||
'pretty_version' => 'v1.32.0',
|
||||
'version' => '1.32.0.0',
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '21533be36c24be3f4b1669c4725c7d1d2bab4ae2',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-uuid',
|
||||
|
@ -568,45 +568,45 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/psr-http-message-bridge' => array(
|
||||
'pretty_version' => 'v6.4.13',
|
||||
'version' => '6.4.13.0',
|
||||
'reference' => 'c9cf83326a1074f83a738fc5320945abf7fb7fec',
|
||||
'pretty_version' => 'v6.4.24',
|
||||
'version' => '6.4.24.0',
|
||||
'reference' => '6954b4e8aef0e5d46f8558c90edcf27bb01b4724',
|
||||
'type' => 'symfony-bridge',
|
||||
'install_path' => __DIR__ . '/../symfony/psr-http-message-bridge',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/service-contracts' => array(
|
||||
'pretty_version' => 'v3.5.1',
|
||||
'version' => '3.5.1.0',
|
||||
'reference' => 'e53260aabf78fb3d63f8d79d69ece59f80d5eda0',
|
||||
'pretty_version' => 'v3.6.0',
|
||||
'version' => '3.6.0.0',
|
||||
'reference' => 'f021b05a130d35510bd6b25fe9053c2a8a15d5d4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/service-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/string' => array(
|
||||
'pretty_version' => 'v6.4.21',
|
||||
'version' => '6.4.21.0',
|
||||
'reference' => '73e2c6966a5aef1d4892873ed5322245295370c6',
|
||||
'pretty_version' => 'v6.4.25',
|
||||
'version' => '6.4.25.0',
|
||||
'reference' => '7cdec7edfaf2cdd9c18901e35bcf9653d6209ff1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/string',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation' => array(
|
||||
'pretty_version' => 'v6.4.21',
|
||||
'version' => '6.4.21.0',
|
||||
'reference' => 'bb92ea5588396b319ba43283a5a3087a034cb29c',
|
||||
'pretty_version' => 'v6.4.24',
|
||||
'version' => '6.4.24.0',
|
||||
'reference' => '300b72643e89de0734d99a9e3f8494a3ef6936e1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-contracts' => array(
|
||||
'pretty_version' => 'v3.5.1',
|
||||
'version' => '3.5.1.0',
|
||||
'reference' => '4667ff3bd513750603a09c8dedbea942487fb07c',
|
||||
'pretty_version' => 'v3.6.0',
|
||||
'version' => '3.6.0.0',
|
||||
'reference' => 'df210c7a2573f1913b2d17cc95f90f53a73d8f7d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||
'aliases' => array(),
|
||||
|
@ -619,9 +619,9 @@
|
|||
),
|
||||
),
|
||||
'symfony/uid' => array(
|
||||
'pretty_version' => 'v6.4.13',
|
||||
'version' => '6.4.13.0',
|
||||
'reference' => '18eb207f0436a993fffbdd811b5b8fa35fa5e007',
|
||||
'pretty_version' => 'v6.4.24',
|
||||
'version' => '6.4.24.0',
|
||||
'reference' => '17da16a750541a42cf2183935e0f6008316c23f7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/uid',
|
||||
'aliases' => array(),
|
||||
|
@ -634,9 +634,9 @@
|
|||
),
|
||||
),
|
||||
'web-auth/cose-lib' => array(
|
||||
'pretty_version' => '4.4.0',
|
||||
'version' => '4.4.0.0',
|
||||
'reference' => '2166016e48e0214f4f63320a7758a9386d14c92a',
|
||||
'pretty_version' => '4.4.2',
|
||||
'version' => '4.4.2.0',
|
||||
'reference' => 'a93b61c48fb587855f64a9ec11ad7b60e867cb15',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../web-auth/cose-lib',
|
||||
'aliases' => array(),
|
||||
|
|
|
@ -38,7 +38,7 @@ class Client
|
|||
const JWT_LEEWAY = 60;
|
||||
const SUCCESS_STATUS_CODE = 200;
|
||||
|
||||
const USER_AGENT = "duo_universal_php/1.1.0";
|
||||
const USER_AGENT = "duo_universal_php/1.1.1";
|
||||
const SIG_ALGORITHM = "HS512";
|
||||
const GRANT_TYPE = "authorization_code";
|
||||
const CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
||||
|
@ -63,6 +63,7 @@ class Client
|
|||
public $redirect_url;
|
||||
public $use_duo_code_attribute;
|
||||
private $client_secret;
|
||||
private $user_agent_extension;
|
||||
|
||||
/**
|
||||
* Retrieves exception message for DuoException from HTTPS result message.
|
||||
|
@ -190,6 +191,34 @@ class Client
|
|||
$this->redirect_url = $redirect_url;
|
||||
$this->use_duo_code_attribute = $use_duo_code_attribute;
|
||||
$this->http_proxy = $http_proxy;
|
||||
$this->user_agent_extension = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append custom information to the user agent string.
|
||||
*
|
||||
* @param string $user_agent_extension Custom user agent information
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function appendToUserAgent(string $user_agent_extension): void
|
||||
{
|
||||
$this->user_agent_extension = trim($user_agent_extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the complete user agent string.
|
||||
*
|
||||
* @return string The complete user agent string
|
||||
*/
|
||||
private function buildUserAgent(): string
|
||||
{
|
||||
$base_user_agent = self::USER_AGENT . " php/" . phpversion() . " "
|
||||
. php_uname();
|
||||
if (!empty($this->user_agent_extension)) {
|
||||
return $base_user_agent . " " . $this->user_agent_extension;
|
||||
}
|
||||
return $base_user_agent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,7 +311,7 @@ class Client
|
|||
public function exchangeAuthorizationCodeFor2FAResult(string $duoCode, string $username, ?string $nonce = null): array
|
||||
{
|
||||
$token_endpoint = "https://" . $this->api_host . self::TOKEN_ENDPOINT;
|
||||
$useragent = self::USER_AGENT . " php/" . phpversion() . " " . php_uname();
|
||||
$useragent = $this->buildUserAgent();
|
||||
$jwt = $this->createJwtPayload($token_endpoint);
|
||||
$request = ["grant_type" => self::GRANT_TYPE,
|
||||
"code" => $duoCode,
|
||||
|
|
|
@ -514,4 +514,144 @@ final class ClientTest extends TestCase
|
|||
$this->assertStringContainsString("scope=openid", $duo_uri);
|
||||
$this->assertStringContainsString($expected_redir_uri, $duo_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the user agent extension can be set and is included in requests.
|
||||
*/
|
||||
public function testAppendToUserAgent(): void
|
||||
{
|
||||
$custom_extension = "MyApp/1.0.0";
|
||||
$id_token = $this->createIdToken();
|
||||
$result = $this->createTokenResult($id_token);
|
||||
|
||||
// Mock the client to capture the user agent sent in HTTP requests
|
||||
$client = $this->getMockBuilder(Client::class)
|
||||
->setConstructorArgs([$this->client_id, $this->client_secret, $this->api_host, $this->redirect_url])
|
||||
->setMethods(['makeHttpsCall'])
|
||||
->getMock();
|
||||
|
||||
// Set up the mock to capture the user agent parameter
|
||||
$captured_user_agent = null;
|
||||
$client->method('makeHttpsCall')
|
||||
->willReturnCallback(function ($endpoint, $request, $user_agent = null) use (&$captured_user_agent, $result) {
|
||||
$captured_user_agent = $user_agent;
|
||||
return $result;
|
||||
});
|
||||
|
||||
// Append custom user agent extension
|
||||
$client->appendToUserAgent($custom_extension);
|
||||
|
||||
// Make a call that uses the user agent
|
||||
$client->exchangeAuthorizationCodeFor2FAResult($this->code, $this->username);
|
||||
|
||||
// Verify the user agent includes our custom extension
|
||||
$this->assertNotNull($captured_user_agent);
|
||||
$this->assertStringContainsString($custom_extension, $captured_user_agent);
|
||||
$this->assertStringContainsString(Client::USER_AGENT, $captured_user_agent);
|
||||
$this->assertStringContainsString("php/" . phpversion(), $captured_user_agent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that user agent works correctly without any extension.
|
||||
*/
|
||||
public function testUserAgentWithoutExtension(): void
|
||||
{
|
||||
$id_token = $this->createIdToken();
|
||||
$result = $this->createTokenResult($id_token);
|
||||
|
||||
// Mock the client to capture the user agent sent in HTTP requests
|
||||
$client = $this->getMockBuilder(Client::class)
|
||||
->setConstructorArgs([$this->client_id, $this->client_secret, $this->api_host, $this->redirect_url])
|
||||
->setMethods(['makeHttpsCall'])
|
||||
->getMock();
|
||||
|
||||
// Set up the mock to capture the user agent parameter
|
||||
$captured_user_agent = null;
|
||||
$client->method('makeHttpsCall')
|
||||
->willReturnCallback(function ($endpoint, $request, $user_agent = null) use (&$captured_user_agent, $result) {
|
||||
$captured_user_agent = $user_agent;
|
||||
return $result;
|
||||
});
|
||||
|
||||
// Make a call without setting any custom user agent extension
|
||||
$client->exchangeAuthorizationCodeFor2FAResult($this->code, $this->username);
|
||||
|
||||
// Verify the user agent contains default information but no custom extension
|
||||
$this->assertNotNull($captured_user_agent);
|
||||
$this->assertStringContainsString(Client::USER_AGENT, $captured_user_agent);
|
||||
$this->assertStringContainsString("php/" . phpversion(), $captured_user_agent);
|
||||
$this->assertStringContainsString(php_uname(), $captured_user_agent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that empty user agent extension is handled correctly.
|
||||
*/
|
||||
public function testAppendToUserAgentEmpty(): void
|
||||
{
|
||||
$id_token = $this->createIdToken();
|
||||
$result = $this->createTokenResult($id_token);
|
||||
|
||||
// Mock the client to capture the user agent sent in HTTP requests
|
||||
$client = $this->getMockBuilder(Client::class)
|
||||
->setConstructorArgs([$this->client_id, $this->client_secret, $this->api_host, $this->redirect_url])
|
||||
->setMethods(['makeHttpsCall'])
|
||||
->getMock();
|
||||
|
||||
// Set up the mock to capture the user agent parameter
|
||||
$captured_user_agent = null;
|
||||
$client->method('makeHttpsCall')
|
||||
->willReturnCallback(function ($endpoint, $request, $user_agent = null) use (&$captured_user_agent, $result) {
|
||||
$captured_user_agent = $user_agent;
|
||||
return $result;
|
||||
});
|
||||
|
||||
// Append empty user agent extension
|
||||
$client->appendToUserAgent("");
|
||||
|
||||
// Make a call
|
||||
$client->exchangeAuthorizationCodeFor2FAResult($this->code, $this->username);
|
||||
|
||||
// Verify the user agent contains default information but no trailing space
|
||||
$this->assertNotNull($captured_user_agent);
|
||||
$this->assertStringContainsString(Client::USER_AGENT, $captured_user_agent);
|
||||
$this->assertStringContainsString("php/" . phpversion(), $captured_user_agent);
|
||||
$this->assertStringContainsString(php_uname(), $captured_user_agent);
|
||||
// Ensure no trailing spaces from empty extension
|
||||
$expected_base = Client::USER_AGENT . " php/" . phpversion() . " " . php_uname();
|
||||
$this->assertEquals($expected_base, $captured_user_agent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that whitespace-only user agent extension is handled correctly.
|
||||
*/
|
||||
public function testAppendToUserAgentWhitespace(): void
|
||||
{
|
||||
$id_token = $this->createIdToken();
|
||||
$result = $this->createTokenResult($id_token);
|
||||
|
||||
// Mock the client to capture the user agent sent in HTTP requests
|
||||
$client = $this->getMockBuilder(Client::class)
|
||||
->setConstructorArgs([$this->client_id, $this->client_secret, $this->api_host, $this->redirect_url])
|
||||
->setMethods(['makeHttpsCall'])
|
||||
->getMock();
|
||||
|
||||
// Set up the mock to capture the user agent parameter
|
||||
$captured_user_agent = null;
|
||||
$client->method('makeHttpsCall')
|
||||
->willReturnCallback(function ($endpoint, $request, $user_agent = null) use (&$captured_user_agent, $result) {
|
||||
$captured_user_agent = $user_agent;
|
||||
return $result;
|
||||
});
|
||||
|
||||
// Append whitespace-only user agent extension
|
||||
$client->appendToUserAgent(" ");
|
||||
|
||||
// Make a call
|
||||
$client->exchangeAuthorizationCodeFor2FAResult($this->code, $this->username);
|
||||
|
||||
// Verify the user agent contains default information but no extra whitespace
|
||||
$this->assertNotNull($captured_user_agent);
|
||||
$expected_base = Client::USER_AGENT . " php/" . phpversion() . " " . php_uname();
|
||||
$this->assertEquals($expected_base, $captured_user_agent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
|
||||
Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
|
||||
|
||||
## 7.10.0 - 2025-08-23
|
||||
|
||||
### Added
|
||||
|
||||
- Support for PHP 8.5
|
||||
|
||||
### Changed
|
||||
|
||||
- Adjusted `guzzlehttp/promises` version constraint to `^2.3`
|
||||
- Adjusted `guzzlehttp/psr7` version constraint to `^2.8`
|
||||
|
||||
|
||||
## 7.9.3 - 2025-03-27
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||
"guzzlehttp/psr7": "^2.7.0",
|
||||
"guzzlehttp/promises": "^2.3",
|
||||
"guzzlehttp/psr7": "^2.8",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
|
|
6
lam/lib/3rdParty/composer/guzzlehttp/guzzle/package-lock.json
generated
vendored
Normal file
6
lam/lib/3rdParty/composer/guzzlehttp/guzzle/package-lock.json
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "guzzle",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
|
@ -125,7 +125,9 @@ class CurlFactory implements CurlFactoryInterface
|
|||
unset($easy->handle);
|
||||
|
||||
if (\count($this->handles) >= $this->maxHandles) {
|
||||
\curl_close($resource);
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
\curl_close($resource);
|
||||
}
|
||||
} else {
|
||||
// Remove all callback functions as they can hold onto references
|
||||
// and are not cleaned up by curl_reset. Using curl_setopt_array
|
||||
|
@ -729,7 +731,10 @@ class CurlFactory implements CurlFactoryInterface
|
|||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $id => $handle) {
|
||||
\curl_close($handle);
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
\curl_close($handle);
|
||||
}
|
||||
|
||||
unset($this->handles[$id]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -240,7 +240,10 @@ class CurlMultiHandler
|
|||
$handle = $this->handles[$id]['easy']->handle;
|
||||
unset($this->delays[$id], $this->handles[$id]);
|
||||
\curl_multi_remove_handle($this->_mh, $handle);
|
||||
\curl_close($handle);
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
\curl_close($handle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -333,8 +333,15 @@ class StreamHandler
|
|||
);
|
||||
|
||||
return $this->createResource(
|
||||
function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) {
|
||||
function () use ($uri, $contextResource, $context, $options, $request) {
|
||||
$resource = @\fopen((string) $uri, 'r', false, $contextResource);
|
||||
|
||||
// See https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable
|
||||
if (function_exists('http_get_last_response_headers')) {
|
||||
/** @var array|null */
|
||||
$http_response_header = \http_get_last_response_headers();
|
||||
}
|
||||
|
||||
$this->lastHeaders = $http_response_header ?? [];
|
||||
|
||||
if (false === $resource) {
|
||||
|
|
|
@ -187,12 +187,12 @@ final class Middleware
|
|||
* Middleware that logs requests, responses, and errors using a message
|
||||
* formatter.
|
||||
*
|
||||
* @phpstan-param \Psr\Log\LogLevel::* $logLevel Level at which to log requests.
|
||||
*
|
||||
* @param LoggerInterface $logger Logs messages.
|
||||
* @param MessageFormatterInterface|MessageFormatter $formatter Formatter used to create message strings.
|
||||
* @param string $logLevel Level at which to log requests.
|
||||
*
|
||||
* @phpstan-param \Psr\Log\LogLevel::* $logLevel Level at which to log requests.
|
||||
*
|
||||
* @return callable Returns a function that accepts the next handler.
|
||||
*/
|
||||
public static function log(LoggerInterface $logger, $formatter, string $logLevel = 'info'): callable
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
# CHANGELOG
|
||||
|
||||
|
||||
## 2.3.0 - 2025-08-22
|
||||
|
||||
### Added
|
||||
|
||||
- PHP 8.5 support
|
||||
|
||||
|
||||
## 2.2.0 - 2025-03-27
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -41,7 +41,7 @@ composer require guzzlehttp/promises
|
|||
| Version | Status | PHP Version |
|
||||
|---------|---------------------|--------------|
|
||||
| 1.x | Security fixes only | >=5.5,<8.3 |
|
||||
| 2.x | Latest | >=7.2.5,<8.5 |
|
||||
| 2.x | Latest | >=7.2.5,<8.6 |
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 2.8.0 - 2025-08-23
|
||||
|
||||
### Added
|
||||
|
||||
- Allow empty lists as header values
|
||||
|
||||
### Changed
|
||||
|
||||
- PHP 8.5 support
|
||||
|
||||
## 2.7.1 - 2025-03-27
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -25,7 +25,7 @@ composer require guzzlehttp/psr7
|
|||
| Version | Status | PHP Version |
|
||||
|---------|---------------------|--------------|
|
||||
| 1.x | EOL (2024-06-30) | >=5.4,<8.2 |
|
||||
| 2.x | Latest | >=7.2.5,<8.5 |
|
||||
| 2.x | Latest | >=7.2.5,<8.6 |
|
||||
|
||||
|
||||
## AppendStream
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"http-interop/http-factory-tests": "0.9.0",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
|
|
|
@ -174,10 +174,6 @@ trait MessageTrait
|
|||
return $this->trimAndValidateHeaderValues([$value]);
|
||||
}
|
||||
|
||||
if (count($value) === 0) {
|
||||
throw new \InvalidArgumentException('Header value can not be an empty array.');
|
||||
}
|
||||
|
||||
return $this->trimAndValidateHeaderValues($value);
|
||||
}
|
||||
|
||||
|
|
|
@ -397,7 +397,7 @@ final class Utils
|
|||
restore_error_handler();
|
||||
|
||||
if ($ex) {
|
||||
/** @var $ex \RuntimeException */
|
||||
/** @var \RuntimeException $ex */
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
|
@ -444,7 +444,7 @@ final class Utils
|
|||
restore_error_handler();
|
||||
|
||||
if ($ex) {
|
||||
/** @var $ex \RuntimeException */
|
||||
/** @var \RuntimeException $ex */
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,21 +44,20 @@
|
|||
"ext-json": "*",
|
||||
"carbonphp/carbon-doctrine-types": "<100.0",
|
||||
"psr/clock": "^1.0",
|
||||
"symfony/clock": "^6.3 || ^7.0",
|
||||
"symfony/clock": "^6.3.12 || ^7.0",
|
||||
"symfony/polyfill-mbstring": "^1.0",
|
||||
"symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0"
|
||||
"symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/dbal": "^3.6.3 || ^4.0",
|
||||
"doctrine/orm": "^2.15.2 || ^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.57.2",
|
||||
"friendsofphp/php-cs-fixer": "^3.75.0",
|
||||
"kylekatarnls/multi-tester": "^2.5.3",
|
||||
"ondrejmirtes/better-reflection": "^6.25.0.4",
|
||||
"phpmd/phpmd": "^2.15.0",
|
||||
"phpstan/extension-installer": "^1.3.1",
|
||||
"phpstan/phpstan": "^1.11.2",
|
||||
"phpunit/phpunit": "^10.5.20",
|
||||
"squizlabs/php_codesniffer": "^3.9.0"
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan": "^2.1.17",
|
||||
"phpunit/phpunit": "^10.5.46",
|
||||
"squizlabs/php_codesniffer": "^3.13.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/clock-implementation": "1.0"
|
||||
|
|
|
@ -127,53 +127,50 @@ This project exists thanks to all the people who contribute.
|
|||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
|
||||
|
||||
<!-- <open-collective-sponsors> -->
|
||||
<a title="Ставки на спорт, БК в Україні" href="https://betking.com.ua/sports-book/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Букмекер" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/065e61d2-f890-42db-b06c-8d40b39b2f0e/bk.jpg" width="96" height="96"></a>
|
||||
<a title="Porównanie kasyn online w Polsce. Darmowe automaty online." href="https://onlinekasyno-polis.pl/" target="_blank"><img alt="Online Kasyno Polis" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/12fe53d4-b2e4-4601-b9ea-7b652c414a38/274px%20274px-2.png" width="96" height="96"></a>
|
||||
<a title="Онлайн казино 777 Україна" href="https://777.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино 777" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7e572d50-1ce8-4d69-ae12-86cc80371373/ok-ua-777.png" width="96" height="96"></a>
|
||||
<a title="Best non Gamstop sites in the UK" href="https://www.pieria.co.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Best non Gamstop sites in the UK" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/34e340b8-e1de-4932-8a76-1b3ce2ec7ee8/logo_white%20bg%20(8).png" width="96" height="96"></a>
|
||||
<a title="Real Money Pokies" href="https://onlinecasinoskiwi.co.nz/real-money-pokies/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Real Money Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d0f7382e-32ea-4425-a8c4-3019f9ed501c/NZ_logo%20(6)%20(2).jpg" width="96" height="96"></a>
|
||||
<a title="Non GamStop Bookies UK" href="https://netto.co.uk/betting-sites-not-on-gamstop/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Non GamStop Bookies UK" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/43c5561c-8907-4ef7-a4ee-c6da054788b8/logo-site%20(3).jpg" width="96" height="96"></a>
|
||||
<a title="#1 Guide To Online Gambling In Canada" href="https://casinohex.org/canada/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="CasinoHex Canada" src="https://opencollective-production.s3.us-west-1.amazonaws.com/79fdbcc0-a997-11eb-abbc-25e48b63c6dc.jpg" width="127.5" height="96"></a>
|
||||
<a title="Non GamStop Bookies UK" href="https://netto.co.uk/betting-sites-not-on-gamstop/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Non GamStop Bookies UK" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/51bfaa05-02b3-4cd9-b1a4-9d0d8f34cbae/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-07-04%20%D0%BE%2015.21.16%20(1)%20(1)%20(1).jpg" width="126" height="96"></a>
|
||||
<a title="Trusted last mile route planning and route optimization" href="https://route4me.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Route4Me Route Planner" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/237386c3-48a2-47c6-97ac-5f888cdb4cda/Route4MeIconLogo.png" width="96" height="96"></a>
|
||||
<a title="Onlinecasinosgr.com" href="https://onlinecasinosgr.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Onlinecasinosgr.com" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/a9b971ee-db5f-4400-8c4b-76cf9bc35015/IMAGE%202024-06-14%2013%3A54%3A14.jpg" width="96" height="96"></a>
|
||||
<a title="Онлайн казино та БК (ставки на спорт) в Україні" href="https://betking.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Betking казино" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/08587758-582c-4136-aba5-2519230960d3/betking.jpg" width="96" height="96"></a>
|
||||
<a title="WestNews – проект Александра Победы о гемблинге и онлайн-казино в Украине, предлагающий новости, обзоры, рейтинги и гиды по игорным заведениям." href="https://westnews.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="WestNews онлайн казино Украины" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7fae83dd-0d53-42f7-b63c-d7062a86ccb1/3502ab17-a150-40e1-8f01-c26ff60c4cf8.png" width="96" height="96"></a>
|
||||
<a title="gaia-wines.gr" href="https://www.gaia-wines.gr/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="gaia-wines.gr" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/a9b971ee-db5f-4400-8c4b-76cf9bc35015/IMAGE%202024-06-14%2013%3A54%3A14.jpg" width="96" height="96"></a>
|
||||
<a title="Ставки на спорт, БК в Україні" href="https://betking.com.ua/sports-book/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Букмекер" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/065e61d2-f890-42db-b06c-8d40b39b2f0e/bk.jpg" width="96" height="96"></a>
|
||||
<a title="Best Casinos not on Gamstop in the UK 2025" href="https://www.vso.org.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="best non Gamstop casinos" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/3f48874e-f2f6-4062-a2a2-1500677ee3d9/125%D1%85125%20(1).jpg" width="96" height="96"></a>
|
||||
<a title="Проект с обзорами легальных онлайн казино Украины. Мы помогаем выбрать лучше казино онлайн игрокам." href="https://sportarena.com/casino/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Лучшие онлайн казино Украины на Sportarena" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/765475f7-3fea-4867-8f83-7b6f91b06128/sportarena%20(1).png" width="60" height="64"></a>
|
||||
<a title="Проєкт з оглядами онлайн казино та їхніх бонусів. На сайті можна знайти актуальні промокоди та інші бонуси онлайн казино України." href="https://y-k.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино та їхні бонуси y-k.com.ua" src="https://logo.clearbit.com/y-k.com.ua" width="64" height="64"></a>
|
||||
<a title="#1 Guide To Online Gambling In Canada" href="https://casinohex.org/canada/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="CasinoHex Canada" src="https://opencollective-production.s3.us-west-1.amazonaws.com/79fdbcc0-a997-11eb-abbc-25e48b63c6dc.jpg" width="127.5" height="96"></a>
|
||||
<a title="Real Money Pokies" href="https://onlinecasinoskiwi.co.nz/real-money-pokies/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Real Money Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d0f7382e-32ea-4425-a8c4-3019f9ed501c/NZ_logo%20(6)%20(2).jpg" width="96" height="96"></a>
|
||||
<a title="Онлайн казино та БК (ставки на спорт) в Україні" href="https://betking.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Betking казино" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/08587758-582c-4136-aba5-2519230960d3/betking.jpg" width="64" height="64"></a>
|
||||
<a title="WestNews – проект Александра Победы о гемблинге и онлайн-казино в Украине, предлагающий новости, обзоры, рейтинги и гиды по игорным заведениям." href="https://westnews.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="WestNews онлайн казино Украины" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7fae83dd-0d53-42f7-b63c-d7062a86ccb1/3502ab17-a150-40e1-8f01-c26ff60c4cf8.png" width="64" height="64"></a>
|
||||
<a title="UK casinos not on GamStop" href="https://www.stjames-theatre.co.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="UK casinos not on GamStop" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/34e5e82e-2121-4082-a321-050dca381d6c/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-01-10%20%D0%BE%2015.29.42%20(1)%20(1).jpg" width="64" height="64"></a><details><summary>See more</summary>
|
||||
<a title="OnlineCasinosSpelen" href="https://onlinecasinosspelen.com?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="OnlineCasinosSpelen" src="https://logo.clearbit.com/onlinecasinosspelen.com" width="64" height="64"></a>
|
||||
<a title="Betwinner is an online bookmaker offering sports betting, casino games, and more." href="https://guidebook.betwinner.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Guidebook.BetWinner" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/82cab29a-7002-4924-83bf-2eecb03d07c4/0x0.png" width="64" height="64"></a>
|
||||
<a title="Онлайн казино casino.ua" href="https://casino.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино casino.ua" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/32790ee6-245b-45bd-acf7-7a661fe2cf9f/logo.png" width="64" height="64"></a>
|
||||
<a title="Best PayID Pokies in Australia" href="https://payid-gambler.net/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="PayIDGambler" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/b120ff74-a4cc-4e25-a96f-2b040d60de14/payidgambler.png" width="64" height="64"></a>
|
||||
<a title="Legal-casino.net – незалежний інтернет-портал, присвячений ліцензійним онлайн казино України та азартним іграм в інтернеті. На якому не проводяться ігри на реальні чи віртуальні гроші." href="https://legal-casino.net/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Legal Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/79978436-a1cb-42f1-8269-d495b232934a/legal-casino.jpg" width="64" height="64"></a>
|
||||
<a title="WildWinz online casino" href="https://wildwinz.com?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="WildWinz Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/ccfcee7c-775c-4d43-ba23-3f0d2969497b/wildwinz.jpg" width="64" height="64"></a>
|
||||
<a title="The Betwinner program allows individuals and businesses to earn commissions." href="https://betwinnerpartner.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Betwinner Partner" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/46a67975-2b70-4b91-9106-0e224c664b21/images%20(12).jpg" width="64" height="64"></a>
|
||||
<a title="Top Casinos Canada" href="https://topcasino.net/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Top Casinos Canada" src="https://topcasino.net/img/topcasino-logo-cover.png" width="64" height="64"></a>
|
||||
<a title="Playfortune.net.br" href="https://playfortune.net.br/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Playfortune.net.br" src="https://logo.clearbit.com/playfortune.net.br" width="64" height="64"></a>
|
||||
<a title="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/" href="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cbeea308-5148-4f6c-ac6e-dbfa029aadd1/PL.png" width="64" height="64"></a>
|
||||
<a title="Best-betting.net is an Indian website where you can always find interesting, useful, and up-to-date information about cricket and other sports. Additionally, on our portal, you can explore predictions and betting opportunities for the most exciting sports" href="https://best-betting.net/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Best Betting" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/4b437e94-747c-4cf5-be67-d11bf8472d76/bestbetting-logo-cover.png" width="64" height="64"></a>
|
||||
<a title="inkedin" href="https://inkedin.com?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="inkedin" src="https://logo.clearbit.com/inkedin.com" width="64" height="64"></a>
|
||||
<a title="Актуальний та повносправний рейтинг онлайн казино України, ґрунтований на відгуках реальних гравців." href="https://uk.onlinecasino.in.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино України" src="https://opencollective-production.s3.us-west-1.amazonaws.com/c0b4b090-eef8-11ec-9cb7-0527a205b226.png" width="64" height="64"></a>
|
||||
<a title="Buy TikTok Followers is a leading provider of social media growth solutions for TikTok.com." href="https://buytiktokfollowers.co/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="BuyTikTokFollowers.co" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/8626c295-9414-4e0c-b228-38ca2704cd68/btf-favicon.png" width="64" height="64"></a>
|
||||
<a title="Slots not on GamStop" href="https://nogamstopcasinos.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Slots not on GamStop" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/3b5fedc2-f3e5-41f5-84a9-869e2cbeb632/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-05-01%20%D0%BE%2019.38.02%20(1)%20(1)%20(1).jpg" width="64" height="64"></a>
|
||||
<a title="Offshore bookmakers review site." href="https://www.sportsbookreviewsonline.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Sportsbook Reviews Online" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/6d499f24-d669-4fc6-bb5f-b87184aa7963/sportsbookreviewsonline_com.png" width="64" height="64"></a>
|
||||
<a title="Ставки на спорт Favbet" href="https://www.favbet.ua/uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Ставки на спорт Favbet" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d86d313e-7b17-42fa-8b76-3f17fbf681a2/favbet-logo.jpg" width="64" height="64"></a>
|
||||
<a title="Znajdź najlepsze zakłady bukmacherskie w Polsce w 2023 roku. Probukmacher.pl to Twoje kompendium wiedzy na temat bukmacherów!" href="https://www.probukmacher.pl?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Probukmacher" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/caf50271-4560-4ffe-a434-ea15239168db/Screenshot_1.png" width="89" height="64"></a>
|
||||
<a title="Casino-portugal.pt" href="https://casino-portugal.pt/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Casino-portugal.pt" src="https://logo.clearbit.com/casino-portugal.pt" width="64" height="64"></a>
|
||||
<a title="inkedin" href="https://inkedin.com?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="inkedin" src="https://logo.clearbit.com/inkedin.com" width="42" height="42"></a>
|
||||
<a title="Casino-portugal.pt" href="https://casino-portugal.pt/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Casino-portugal.pt" src="https://logo.clearbit.com/casino-portugal.pt" width="42" height="42"></a>
|
||||
<a title="Znajdź najlepsze zakłady bukmacherskie w Polsce w 2023 roku. Probukmacher.pl to Twoje kompendium wiedzy na temat bukmacherów!" href="https://www.probukmacher.pl?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Probukmacher" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/caf50271-4560-4ffe-a434-ea15239168db/Screenshot_1.png" width="58" height="42"></a>
|
||||
<a title="Get professional support for Carbon" href="https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=docs" target="_blank"><img alt="Tidelift" src="https://carbon.nesbot.com/docs/sponsors/tidelift-brand.png" width="84" height="42"></a>
|
||||
<a title="Playfortune.net.br" href="https://playfortune.net.br/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Playfortune.net.br" src="https://logo.clearbit.com/playfortune.net.br" width="42" height="42"></a>
|
||||
<a title="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/" href="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="https://play-fortune.pl/kasyno/z-minimalnym-depozytem/" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cbeea308-5148-4f6c-ac6e-dbfa029aadd1/PL.png" width="42" height="42"></a>
|
||||
<a title="UK casinos not on GamStop" href="https://www.stjamestheatre.co.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="UK casinos not on GamStop" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/34e5e82e-2121-4082-a321-050dca381d6c/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-01-10%20%D0%BE%2015.29.42%20(1)%20(1).jpg" width="42" height="42"></a>
|
||||
<a title="Актуальний та повносправний рейтинг онлайн казино України, ґрунтований на відгуках реальних гравців." href="https://uk.onlinecasino.in.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино України" src="https://opencollective-production.s3.us-west-1.amazonaws.com/c0b4b090-eef8-11ec-9cb7-0527a205b226.png" width="42" height="42"></a>
|
||||
<a title="Sites not on GamStop" href="https://casinonotongamstop.uk/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Sites not on GamStop" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/5c5977b8-1e94-43d6-b2d7-4af25bb85dbd/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-05-01%20%D0%BE%2015.08.38%20(1)%20(2).jpg" width="68" height="42"></a>
|
||||
<a title="Проект с обзорами легальных онлайн казино Украины. Мы помогаем выбрать лучше казино онлайн игрокам." href="https://sportarena.com/casino/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Лучшие онлайн казино Украины на Sportarena" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/765475f7-3fea-4867-8f83-7b6f91b06128/sportarena%20(1).png" width="40" height="42"></a>
|
||||
<a title="Проєкт з оглядами онлайн казино та їхніх бонусів. На сайті можна знайти актуальні промокоди та інші бонуси онлайн казино України." href="https://y-k.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино та їхні бонуси y-k.com.ua" src="https://logo.clearbit.com/y-k.com.ua" width="42" height="42"></a>
|
||||
<a title="Slots City® ➢ Лучшее лицензионно казино онлайн и оффлайн на гривны в Украине. 【 Более1500 игровых автоматов и слотов】✅ Официально и Безопасно" href="https://slotscity.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Slots City" src="https://opencollective-production.s3.us-west-1.amazonaws.com/d7e298c0-7abe-11ed-8553-230872f5e54d.png" width="59" height="42"></a>
|
||||
<a title="Entertainment" href="https://www.nongamstopbets.com/casinos-not-on-gamstop/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Non-GamStop Bets UK" src="https://logo.clearbit.com/nongamstopbets.com" width="42" height="42"></a>
|
||||
<a title="WildWinz online casino" href="https://wildwinz.com?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="WildWinz Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/ccfcee7c-775c-4d43-ba23-3f0d2969497b/wildwinz.jpg" width="42" height="42"></a>
|
||||
<a title="ігрові автомати беткінг" href="https://betking.com.ua/games/all-slots/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Ігрові автомати" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/94601d07-3205-4c60-9c2d-9b8194dbefb7/skg-blue.png" width="42" height="42"></a>
|
||||
<a title="Casinos not on Gamstop" href="https://lgcnews.com/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Non Gamstop Casinos" src="https://lgcnews.com/wp-content/uploads/2018/01/LGC-logo-v8-temp.png" width="84" height="42"></a>
|
||||
<a title="Slotozilla website" href="https://www.slotozilla.com/nz/free-spins" target="_blank"><img alt="Slotozilla" src="https://carbon.nesbot.com/docs/sponsors/slotozilla.png" width="42" height="42"></a>
|
||||
<a title="Per tutte le ultime notizie sul gioco d'azzardo Non AAMS, le recensioni e i bonus di iscrizione." href="https://casinononaams.online?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="casino non aams" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/c60b92d1-590c-48a5-9527-fb0909431a86/casino%20non%20aams%20icon.jpg" width="42" height="42"></a>
|
||||
<a title="Credit Zaim" href="https://creditzaim.com.ua/?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Credit Zaim" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/a856ed4e-651d-47c9-aa7a-98059423b3a6/creditzaim_logo.png" width="42" height="42"></a>
|
||||
<a title="Incognito" href="https://opencollective.com/user-7146c9f8?utm_source=opencollective&utm_medium=github&utm_campaign=Carbon" target="_blank"><img alt="Incognito" src="https://images.opencollective.com/user-7146c9f8/avatar/256.png" width="42" height="42"></a><!-- </open-collective-sponsors> -->
|
||||
<a title="ssddanbrown" href="https://github.com/ssddanbrown" target="_blank"><img alt="ssddanbrown" src="https://avatars.githubusercontent.com/u/8343178?s=128&v=4" width="42" height="42"></a></details><!-- </open-collective-sponsors> -->
|
||||
|
||||
[[See all](https://carbon.nesbot.com/#sponsors)]
|
||||
[[Become a sponsor via OpenCollective*](https://opencollective.com/Carbon#sponsor)]
|
||||
|
||||
<a href="https://github.com/ssddanbrown" target="_blank"><img src="https://avatars.githubusercontent.com/u/8343178?s=128&v=4" width="42" height="42"></a>
|
||||
<a href="https://github.com/BallymaloeCookerySchool" target="_blank"><img src="https://avatars.githubusercontent.com/u/123261043?s=128&v=4" width="42" height="42"></a>
|
||||
[[Become a sponsor via OpenCollective*](https://opencollective.com/Carbon#sponsor)]
|
||||
|
||||
[[Become a sponsor via GitHub*](https://github.com/sponsors/kylekatarnls)]
|
||||
|
||||
|
|
|
@ -158,7 +158,10 @@ function getOpenCollectiveSponsors(): string
|
|||
$status = null;
|
||||
$rank = 0;
|
||||
|
||||
if ($monthlyContribution > 29 || $yearlyContribution > 700) {
|
||||
if ($monthlyContribution > 50 || $yearlyContribution > 900) {
|
||||
$status = 'sponsor';
|
||||
$rank = 5;
|
||||
} elseif ($monthlyContribution > 29 || $yearlyContribution > 700) {
|
||||
$status = 'sponsor';
|
||||
$rank = 4;
|
||||
} elseif ($monthlyContribution > 14.5 || $yearlyContribution > 500) {
|
||||
|
@ -190,6 +193,7 @@ function getOpenCollectiveSponsors(): string
|
|||
|
||||
$membersByUrl = [];
|
||||
$output = '';
|
||||
$extra = '';
|
||||
|
||||
foreach ($list as $member) {
|
||||
$url = $member['website'] ?? $member['profile'];
|
||||
|
@ -229,12 +233,30 @@ function getOpenCollectiveSponsors(): string
|
|||
$height *= 1.5;
|
||||
}
|
||||
|
||||
$output .= "\n".'<a title="'.$title.'" href="'.$href.'" target="_blank"'.$rel.'>'.
|
||||
$link = "\n".'<a title="'.$title.'" href="'.$href.'" target="_blank"'.$rel.'>'.
|
||||
'<img alt="'.$alt.'" src="'.$src.'" width="'.$width.'" height="'.$height.'">'.
|
||||
'</a>';
|
||||
|
||||
if ($member['rank'] >= 5) {
|
||||
$output .= $link;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$extra .= $link;
|
||||
}
|
||||
|
||||
$github = [
|
||||
8343178 => 'ssddanbrown',
|
||||
];
|
||||
|
||||
foreach ($github as $avatar => $user) {
|
||||
$extra .= "\n".'<a title="'.$user.'" href="https://github.com/'.$user.'" target="_blank">'.
|
||||
'<img alt="'.$user.'" src="https://avatars.githubusercontent.com/u/'.$avatar.'?s=128&v=4" width="42" height="42">'.
|
||||
'</a>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
return $output.'<details><summary>See more</summary>'.$extra.'</details>';
|
||||
}
|
||||
|
||||
file_put_contents('readme.md', preg_replace_callback(
|
||||
|
|
|
@ -533,7 +533,7 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
($totalDays - $this->d).' days '.
|
||||
($hours - $this->h).' hours '.
|
||||
($minutes - $this->i).' minutes '.
|
||||
($intervalSeconds - $this->s).' seconds '.
|
||||
number_format($intervalSeconds - $this->s, 6, '.', '').' seconds '.
|
||||
($microseconds - $intervalMicroseconds).' microseconds ',
|
||||
));
|
||||
}
|
||||
|
@ -1082,7 +1082,7 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
|
||||
default:
|
||||
throw new InvalidIntervalException(
|
||||
\sprintf('Invalid part %s in definition %s', $part, $intervalDefinition),
|
||||
"Invalid part $part in definition $intervalDefinition",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3088,16 +3088,50 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
|
||||
// PHP <= 8.1
|
||||
// @codeCoverageIgnoreStart
|
||||
foreach ($properties as $property => $value) {
|
||||
$name = preg_replace('/^\0.+\0/', '', $property);
|
||||
$localStrictMode = $this->localStrictModeEnabled;
|
||||
$this->localStrictModeEnabled = false;
|
||||
$this->$name = $value;
|
||||
$properties = array_combine(
|
||||
array_map(
|
||||
static fn (string $property) => preg_replace('/^\0.+\0/', '', $property),
|
||||
array_keys($data),
|
||||
),
|
||||
$data,
|
||||
);
|
||||
$localStrictMode = $this->localStrictModeEnabled;
|
||||
$this->localStrictModeEnabled = false;
|
||||
$days = $properties['days'] ?? false;
|
||||
$this->days = $days === false ? false : (int) $days;
|
||||
$this->y = (int) ($properties['y'] ?? 0);
|
||||
$this->m = (int) ($properties['m'] ?? 0);
|
||||
$this->d = (int) ($properties['d'] ?? 0);
|
||||
$this->h = (int) ($properties['h'] ?? 0);
|
||||
$this->i = (int) ($properties['i'] ?? 0);
|
||||
$this->s = (int) ($properties['s'] ?? 0);
|
||||
$this->f = (float) ($properties['f'] ?? 0.0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->weekday = (int) ($properties['weekday'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->weekday_behavior = (int) ($properties['weekday_behavior'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->first_last_day_of = (int) ($properties['first_last_day_of'] ?? 0);
|
||||
$this->invert = (int) ($properties['invert'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->special_type = (int) ($properties['special_type'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->special_amount = (int) ($properties['special_amount'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->have_weekday_relative = (int) ($properties['have_weekday_relative'] ?? 0);
|
||||
// @phpstan-ignore-next-line
|
||||
$this->have_special_relative = (int) ($properties['have_special_relative'] ?? 0);
|
||||
parent::__construct(self::getDateIntervalSpec($this));
|
||||
|
||||
if ($name !== 'localStrictModeEnabled') {
|
||||
$this->localStrictModeEnabled = $localStrictMode;
|
||||
foreach ($properties as $property => $value) {
|
||||
if ($property === 'localStrictModeEnabled') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->$property = $value;
|
||||
}
|
||||
|
||||
$this->localStrictModeEnabled = $properties['localStrictModeEnabled'] ?? $localStrictMode;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
|
@ -3156,7 +3190,7 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
}
|
||||
|
||||
$microseconds = $interval->f;
|
||||
$instance = new $className(static::getDateIntervalSpec($interval, false, $skip));
|
||||
$instance = self::buildInstance($interval, $className, $skip);
|
||||
|
||||
if ($instance instanceof self) {
|
||||
$instance->originalInput = $interval;
|
||||
|
@ -3175,6 +3209,83 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
return self::withOriginal($instance, $interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of DateInterval
|
||||
*
|
||||
* @param DateInterval $interval
|
||||
*
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @return T
|
||||
*/
|
||||
private static function buildInstance(
|
||||
DateInterval $interval,
|
||||
string $className,
|
||||
array $skip = [],
|
||||
): object {
|
||||
$serialization = self::buildSerializationString($interval, $className, $skip);
|
||||
|
||||
return match ($serialization) {
|
||||
null => new $className(static::getDateIntervalSpec($interval, false, $skip)),
|
||||
default => unserialize($serialization),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* As demonstrated by rlanvin (https://github.com/rlanvin) in
|
||||
* https://github.com/briannesbitt/Carbon/issues/3018#issuecomment-2888538438
|
||||
*
|
||||
* Modifying the output of serialize() to change the class name and unserializing
|
||||
* the tweaked string allows creating new interval instances where the ->days
|
||||
* property can be set. It's not possible neither with `new` nto with `__set_state`.
|
||||
*
|
||||
* It has a non-negligible performance cost, so we'll use this method only if
|
||||
* $interval->days !== false.
|
||||
*/
|
||||
private static function buildSerializationString(
|
||||
DateInterval $interval,
|
||||
string $className,
|
||||
array $skip = [],
|
||||
): ?string {
|
||||
if ($interval->days === false || PHP_VERSION_ID < 8_02_00 || $skip !== []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// De-enhance CarbonInterval objects to be serializable back to DateInterval
|
||||
if ($interval instanceof self && !is_a($className, self::class, true)) {
|
||||
$interval = clone $interval;
|
||||
unset($interval->timezoneSetting);
|
||||
unset($interval->originalInput);
|
||||
unset($interval->startDate);
|
||||
unset($interval->endDate);
|
||||
unset($interval->rawInterval);
|
||||
unset($interval->absolute);
|
||||
unset($interval->initialValues);
|
||||
unset($interval->clock);
|
||||
unset($interval->step);
|
||||
unset($interval->localMonthsOverflow);
|
||||
unset($interval->localYearsOverflow);
|
||||
unset($interval->localStrictModeEnabled);
|
||||
unset($interval->localHumanDiffOptions);
|
||||
unset($interval->localToStringFormat);
|
||||
unset($interval->localSerializer);
|
||||
unset($interval->localMacros);
|
||||
unset($interval->localGenericMacros);
|
||||
unset($interval->localFormatFunction);
|
||||
unset($interval->localTranslator);
|
||||
}
|
||||
|
||||
$serialization = serialize($interval);
|
||||
$inputClass = $interval::class;
|
||||
$expectedStart = 'O:'.\strlen($inputClass).':"'.$inputClass.'":';
|
||||
|
||||
if (!str_starts_with($serialization, $expectedStart)) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return 'O:'.\strlen($className).':"'.$className.'":'.substr($serialization, \strlen($expectedStart));
|
||||
}
|
||||
|
||||
private static function copyStep(self $from, self $to): void
|
||||
{
|
||||
$to->setStep($from->getStep());
|
||||
|
@ -3408,7 +3519,8 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
return;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID !== 80320) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if (PHP_VERSION_ID !== 8_03_20) {
|
||||
$instance->$unit += $value;
|
||||
|
||||
return;
|
||||
|
@ -3416,8 +3528,10 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface
|
|||
|
||||
// Cannot use +=, nor set to a negative value directly as it segfaults in PHP 8.3.20
|
||||
self::setIntervalUnit($instance, $unit, ($instance->$unit ?? 0) + $value);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @codeCoverageIgnore */
|
||||
private static function setIntervalUnit(DateInterval $instance, string $unit, mixed $value): void
|
||||
{
|
||||
switch ($unit) {
|
||||
|
|
|
@ -176,9 +176,9 @@ require PHP_VERSION < 8.2
|
|||
*
|
||||
* @mixin DeprecatedPeriodProperties
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.TooManyFields)
|
||||
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
* @SuppressWarnings(TooManyFields)
|
||||
* @SuppressWarnings(CamelCasePropertyName)
|
||||
* @SuppressWarnings(CouplingBetweenObjects)
|
||||
*/
|
||||
class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
||||
{
|
||||
|
@ -414,16 +414,19 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
|
||||
$instance = static::createFromArray($params);
|
||||
|
||||
if ($options !== null) {
|
||||
$instance->options = $options;
|
||||
$instance->handleChangedParameters();
|
||||
}
|
||||
$instance->options = ($instance instanceof CarbonPeriodImmutable ? static::IMMUTABLE : 0) | $options;
|
||||
$instance->handleChangedParameters();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function createFromISO8601String(string $iso, ?int $options = null): static
|
||||
{
|
||||
return self::createFromIso($iso, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether given interval contains non zero value of any time unit.
|
||||
* Return whether the given interval contains non-zero value of any time unit.
|
||||
*/
|
||||
protected static function intervalHasTime(DateInterval $interval): bool
|
||||
{
|
||||
|
@ -453,7 +456,7 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
/**
|
||||
* Parse given ISO 8601 string into an array of arguments.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ElseExpression)
|
||||
* @SuppressWarnings(ElseExpression)
|
||||
*/
|
||||
protected static function parseIso8601(string $iso): array
|
||||
{
|
||||
|
@ -597,7 +600,7 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
/**
|
||||
* CarbonPeriod constructor.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ElseExpression)
|
||||
* @SuppressWarnings(ElseExpression)
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
|
@ -725,9 +728,10 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
parent::__construct(
|
||||
$this->startDate,
|
||||
$this->dateInterval,
|
||||
$this->endDate ?? $this->recurrences ?? 1,
|
||||
$this->endDate ?? max(1, min(2147483639, $this->recurrences ?? 1)),
|
||||
$this->options,
|
||||
);
|
||||
|
||||
$this->constructed = true;
|
||||
}
|
||||
|
||||
|
@ -1115,7 +1119,7 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
/**
|
||||
* Add a filter to the stack.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
* @SuppressWarnings(UnusedFormalParameter)
|
||||
*/
|
||||
public function addFilter(callable|string $callback, ?string $name = null): static
|
||||
{
|
||||
|
@ -1132,7 +1136,7 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
/**
|
||||
* Prepend a filter to the stack.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
* @SuppressWarnings(UnusedFormalParameter)
|
||||
*/
|
||||
public function prependFilter(callable|string $callback, ?string $name = null): static
|
||||
{
|
||||
|
@ -2245,11 +2249,100 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
public function __debugInfo(): array
|
||||
{
|
||||
$info = $this->baseDebugInfo();
|
||||
unset($info['start'], $info['end'], $info['interval'], $info['include_start_date'], $info['include_end_date']);
|
||||
unset(
|
||||
$info['start'],
|
||||
$info['end'],
|
||||
$info['interval'],
|
||||
$info['include_start_date'],
|
||||
$info['include_end_date'],
|
||||
$info['constructed'],
|
||||
$info["\0*\0constructed"],
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
try {
|
||||
$values = array_combine(
|
||||
array_map(
|
||||
static fn (string $key): string => preg_replace('/^\0\*\0/', '', $key),
|
||||
array_keys($data),
|
||||
),
|
||||
$data,
|
||||
);
|
||||
|
||||
$this->initializeSerialization($values);
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if ($value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property = match ($key) {
|
||||
'tzName' => $this->setTimezone(...),
|
||||
'options' => $this->setOptions(...),
|
||||
'recurrences' => $this->setRecurrences(...),
|
||||
'current' => function (mixed $current): void {
|
||||
if (!($current instanceof CarbonInterface)) {
|
||||
$current = $this->resolveCarbon($current);
|
||||
}
|
||||
|
||||
$this->carbonCurrent = $current;
|
||||
},
|
||||
'start' => 'startDate',
|
||||
'interval' => $this->setDateInterval(...),
|
||||
'end' => 'endDate',
|
||||
'key' => null,
|
||||
'include_start_date' => function (bool $included): void {
|
||||
$this->excludeStartDate(!$included);
|
||||
},
|
||||
'include_end_date' => function (bool $included): void {
|
||||
$this->excludeEndDate(!$included);
|
||||
},
|
||||
default => $key,
|
||||
};
|
||||
|
||||
if ($property === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\is_callable($property)) {
|
||||
$property($value);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value instanceof DateTimeInterface && !($value instanceof CarbonInterface)) {
|
||||
$value = ($value instanceof DateTime)
|
||||
? Carbon::instance($value)
|
||||
: CarbonImmutable::instance($value);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->$property = $value;
|
||||
} catch (Throwable) {
|
||||
// Must be ignored for backward-compatibility
|
||||
}
|
||||
}
|
||||
|
||||
if (\array_key_exists('carbonRecurrences', $values)) {
|
||||
$this->carbonRecurrences = $values['carbonRecurrences'];
|
||||
} elseif (((int) ($values['recurrences'] ?? 0)) <= 1 && $this->endDate !== null) {
|
||||
$this->carbonRecurrences = null;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!method_exists(parent::class, '__unserialize')) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
parent::__unserialize($data);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update properties after removing built-in filters.
|
||||
*/
|
||||
|
@ -2293,7 +2386,7 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
/**
|
||||
* Recurrences filter callback (limits number of recurrences).
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
* @SuppressWarnings(UnusedFormalParameter)
|
||||
*/
|
||||
protected function filterRecurrences(CarbonInterface $current, int $key): bool|callable
|
||||
{
|
||||
|
@ -2578,4 +2671,47 @@ class CarbonPeriod extends DatePeriodBase implements Countable, JsonSerializable
|
|||
|
||||
return $sortedArguments;
|
||||
}
|
||||
|
||||
private function initializeSerialization(array $values): void
|
||||
{
|
||||
$serializationBase = [
|
||||
'start' => $values['start'] ?? $values['startDate'] ?? null,
|
||||
'current' => $values['current'] ?? $values['carbonCurrent'] ?? null,
|
||||
'end' => $values['end'] ?? $values['endDate'] ?? null,
|
||||
'interval' => $values['interval'] ?? $values['dateInterval'] ?? null,
|
||||
'recurrences' => max(1, (int) ($values['recurrences'] ?? $values['carbonRecurrences'] ?? 1)),
|
||||
'include_start_date' => $values['include_start_date'] ?? true,
|
||||
'include_end_date' => $values['include_end_date'] ?? false,
|
||||
];
|
||||
|
||||
foreach (['start', 'current', 'end'] as $dateProperty) {
|
||||
if ($serializationBase[$dateProperty] instanceof Carbon) {
|
||||
$serializationBase[$dateProperty] = $serializationBase[$dateProperty]->toDateTime();
|
||||
} elseif ($serializationBase[$dateProperty] instanceof CarbonInterface) {
|
||||
$serializationBase[$dateProperty] = $serializationBase[$dateProperty]->toDateTimeImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
if ($serializationBase['interval'] instanceof CarbonInterval) {
|
||||
$serializationBase['interval'] = $serializationBase['interval']->toDateInterval();
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (method_exists(parent::class, '__unserialize')) {
|
||||
parent::__unserialize($serializationBase);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$excludeStart = !($values['include_start_date'] ?? true);
|
||||
$includeEnd = $values['include_end_date'] ?? true;
|
||||
|
||||
parent::__construct(
|
||||
$serializationBase['start'],
|
||||
$serializationBase['interval'],
|
||||
$serializationBase['end'] ?? $serializationBase['recurrences'],
|
||||
($excludeStart ? self::EXCLUDE_START_DATE : 0) | ($includeEnd && \defined('DatePeriod::INCLUDE_END_DATE') ? self::INCLUDE_END_DATE : 0),
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Carbon;
|
|||
use Carbon\Exceptions\InvalidCastException;
|
||||
use Carbon\Exceptions\InvalidTimeZoneException;
|
||||
use Carbon\Traits\LocalFactory;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
|
@ -129,10 +130,23 @@ class CarbonTimeZone extends DateTimeZone
|
|||
{
|
||||
$name = $this->getName();
|
||||
|
||||
foreach ($this->listAbbreviations() as $abbreviation => $zones) {
|
||||
$date = new DateTimeImmutable($dst ? 'July 1' : 'January 1', $this);
|
||||
$timezone = $date->format('T');
|
||||
$abbreviations = $this->listAbbreviations();
|
||||
$matchingZones = array_merge($abbreviations[$timezone] ?? [], $abbreviations[strtolower($timezone)] ?? []);
|
||||
|
||||
if ($matchingZones !== []) {
|
||||
foreach ($matchingZones as $zone) {
|
||||
if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) {
|
||||
return $timezone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($abbreviations as $abbreviation => $zones) {
|
||||
foreach ($zones as $zone) {
|
||||
if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) {
|
||||
return $abbreviation;
|
||||
return strtoupper($abbreviation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,25 +22,25 @@
|
|||
*/
|
||||
return [
|
||||
'year' => ':count il',
|
||||
'a_year' => '{1}bir il|]1,Inf[:count il',
|
||||
'a_year' => '{1}bir il|[-Inf,Inf]:count il',
|
||||
'y' => ':count il',
|
||||
'month' => ':count ay',
|
||||
'a_month' => '{1}bir ay|]1,Inf[:count ay',
|
||||
'a_month' => '{1}bir ay|[-Inf,Inf]:count ay',
|
||||
'm' => ':count ay',
|
||||
'week' => ':count həftə',
|
||||
'a_week' => '{1}bir həftə|]1,Inf[:count həftə',
|
||||
'a_week' => '{1}bir həftə|[-Inf,Inf]:count həftə',
|
||||
'w' => ':count h.',
|
||||
'day' => ':count gün',
|
||||
'a_day' => '{1}bir gün|]1,Inf[:count gün',
|
||||
'a_day' => '{1}bir gün|[-Inf,Inf]:count gün',
|
||||
'd' => ':count g.',
|
||||
'hour' => ':count saat',
|
||||
'a_hour' => '{1}bir saat|]1,Inf[:count saat',
|
||||
'a_hour' => '{1}bir saat|[-Inf,Inf]:count saat',
|
||||
'h' => ':count s.',
|
||||
'minute' => ':count dəqiqə',
|
||||
'a_minute' => '{1}bir dəqiqə|]1,Inf[:count dəqiqə',
|
||||
'a_minute' => '{1}bir dəqiqə|[-Inf,Inf]:count dəqiqə',
|
||||
'min' => ':count d.',
|
||||
'second' => ':count saniyə',
|
||||
'a_second' => '{1}birneçə saniyə|]1,Inf[:count saniyə',
|
||||
'a_second' => '{1}birneçə saniyə|[-Inf,Inf]:count saniyə',
|
||||
's' => ':count san.',
|
||||
'ago' => ':time əvvəl',
|
||||
'from_now' => ':time sonra',
|
||||
|
|
|
@ -15,13 +15,20 @@
|
|||
* - JD Isaacks
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}ལོ་གཅིག|]1,Inf[:count ལོ',
|
||||
'month' => '{1}ཟླ་བ་གཅིག|]1,Inf[:count ཟླ་བ',
|
||||
'week' => ':count བདུན་ཕྲག',
|
||||
'day' => '{1}ཉིན་གཅིག|]1,Inf[:count ཉིན་',
|
||||
'hour' => '{1}ཆུ་ཚོད་གཅིག|]1,Inf[:count ཆུ་ཚོད',
|
||||
'minute' => '{1}སྐར་མ་གཅིག|]1,Inf[:count སྐར་མ',
|
||||
'second' => '{1}ལམ་སང|]1,Inf[:count སྐར་ཆ།',
|
||||
'year' => 'ལོ:count',
|
||||
'a_year' => '{1}ལོ་གཅིག|[-Inf,Inf]ལོ:count',
|
||||
'month' => 'ཟླ་བ:count',
|
||||
'a_month' => '{1}ཟླ་བ་གཅིག|[-Inf,Inf]ཟླ་བ:count',
|
||||
'week' => 'གཟའ་འཁོར་:count',
|
||||
'a_week' => 'གཟའ་འཁོར་གཅིག',
|
||||
'day' => 'ཉིན:count་',
|
||||
'a_day' => '{1}ཉིན་གཅིག|[-Inf,Inf]ཉིན:count',
|
||||
'hour' => 'ཆུ་ཚོད:count',
|
||||
'a_hour' => '{1}ཆུ་ཚོད་གཅིག|[-Inf,Inf]ཆུ་ཚོད:count',
|
||||
'minute' => 'སྐར་མ་:count',
|
||||
'a_minute' => '{1}སྐར་མ་གཅིག|[-Inf,Inf]སྐར་མ་:count',
|
||||
'second' => 'སྐར་ཆ:count',
|
||||
'a_second' => '{01}ལམ་སང|[-Inf,Inf]སྐར་ཆ:count',
|
||||
'ago' => ':time སྔན་ལ',
|
||||
'from_now' => ':time ལ་',
|
||||
'diff_yesterday' => 'ཁ་སང',
|
||||
|
|
|
@ -16,19 +16,26 @@
|
|||
* - Daniel Monaghan
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}blwyddyn|]1,Inf[:count flynedd',
|
||||
'year' => '{1}:count flwyddyn|[-Inf,Inf]:count flynedd',
|
||||
'a_year' => '{1}blwyddyn|[-Inf,Inf]:count flynedd',
|
||||
'y' => ':countbl',
|
||||
'month' => '{1}mis|]1,Inf[:count mis',
|
||||
'month' => ':count mis',
|
||||
'a_month' => '{1}mis|[-Inf,Inf]:count mis',
|
||||
'm' => ':countmi',
|
||||
'week' => ':count wythnos',
|
||||
'a_week' => '{1}wythnos|[-Inf,Inf]:count wythnos',
|
||||
'w' => ':countw',
|
||||
'day' => '{1}diwrnod|]1,Inf[:count diwrnod',
|
||||
'day' => ':count diwrnod',
|
||||
'a_day' => '{1}diwrnod|[-Inf,Inf]:count diwrnod',
|
||||
'd' => ':countd',
|
||||
'hour' => '{1}awr|]1,Inf[:count awr',
|
||||
'hour' => ':count awr',
|
||||
'a_hour' => '{1}awr|[-Inf,Inf]:count awr',
|
||||
'h' => ':counth',
|
||||
'minute' => '{1}munud|]1,Inf[:count munud',
|
||||
'minute' => ':count munud',
|
||||
'a_minute' => '{1}munud|[-Inf,Inf]:count munud',
|
||||
'min' => ':countm',
|
||||
'second' => '{1}ychydig eiliadau|]1,Inf[:count eiliad',
|
||||
'second' => ':count eiliad',
|
||||
'a_second' => '{0,1}ychydig eiliadau|[-Inf,Inf]:count eiliad',
|
||||
's' => ':counts',
|
||||
'ago' => ':time yn ôl',
|
||||
'from_now' => 'mewn :time',
|
||||
|
|
|
@ -10,18 +10,18 @@
|
|||
*/
|
||||
|
||||
$months = [
|
||||
'ޖެނުއަރީ',
|
||||
'ފެބްރުއަރީ',
|
||||
'ޖަނަވަރީ',
|
||||
'ފެބުރުވަރީ',
|
||||
'މާރިޗު',
|
||||
'އޭޕްރީލު',
|
||||
'އެޕްރީލް',
|
||||
'މޭ',
|
||||
'ޖޫން',
|
||||
'ޖުލައި',
|
||||
'އޯގަސްޓު',
|
||||
'ސެޕްޓެމްބަރު',
|
||||
'އޮކްޓޯބަރު',
|
||||
'ނޮވެމްބަރު',
|
||||
'ޑިސެމްބަރު',
|
||||
'އޮގަސްޓު',
|
||||
'ސެޕްޓެންބަރު',
|
||||
'އޮކްޓޫބަރު',
|
||||
'ނޮވެންބަރު',
|
||||
'ޑިސެންބަރު',
|
||||
];
|
||||
|
||||
$weekdays = [
|
||||
|
@ -38,6 +38,7 @@ $weekdays = [
|
|||
* Authors:
|
||||
* - Josh Soref
|
||||
* - Jawish Hameed
|
||||
* - Saiph Muhammad
|
||||
*/
|
||||
return [
|
||||
'year' => ':count '.'އަހަރު',
|
||||
|
|
|
@ -17,37 +17,37 @@
|
|||
*/
|
||||
return [
|
||||
/*
|
||||
* {1}, {0} and ]1,Inf[ are not needed as it's the default for English pluralization.
|
||||
* {1}, {0} and [-Inf,Inf] are not needed as it's the default for English pluralization.
|
||||
* But as some languages are using en.php as a fallback, it's better to specify it
|
||||
* explicitly so those languages also fallback to English pluralization when a unit
|
||||
* is missing.
|
||||
*/
|
||||
'year' => '{1}:count year|{0}:count years|]1,Inf[:count years',
|
||||
'a_year' => '{1}a year|{0}:count years|]1,Inf[:count years',
|
||||
'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs',
|
||||
'month' => '{1}:count month|{0}:count months|]1,Inf[:count months',
|
||||
'a_month' => '{1}a month|{0}:count months|]1,Inf[:count months',
|
||||
'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos',
|
||||
'week' => '{1}:count week|{0}:count weeks|]1,Inf[:count weeks',
|
||||
'a_week' => '{1}a week|{0}:count weeks|]1,Inf[:count weeks',
|
||||
'year' => '{1}:count year|{0}:count years|[-Inf,Inf]:count years',
|
||||
'a_year' => '{1}a year|{0}:count years|[-Inf,Inf]:count years',
|
||||
'y' => '{1}:countyr|{0}:countyrs|[-Inf,Inf]:countyrs',
|
||||
'month' => '{1}:count month|{0}:count months|[-Inf,Inf]:count months',
|
||||
'a_month' => '{1}a month|{0}:count months|[-Inf,Inf]:count months',
|
||||
'm' => '{1}:countmo|{0}:countmos|[-Inf,Inf]:countmos',
|
||||
'week' => '{1}:count week|{0}:count weeks|[-Inf,Inf]:count weeks',
|
||||
'a_week' => '{1}a week|{0}:count weeks|[-Inf,Inf]:count weeks',
|
||||
'w' => ':countw',
|
||||
'day' => '{1}:count day|{0}:count days|]1,Inf[:count days',
|
||||
'a_day' => '{1}a day|{0}:count days|]1,Inf[:count days',
|
||||
'day' => '{1}:count day|{0}:count days|[-Inf,Inf]:count days',
|
||||
'a_day' => '{1}a day|{0}:count days|[-Inf,Inf]:count days',
|
||||
'd' => ':countd',
|
||||
'hour' => '{1}:count hour|{0}:count hours|]1,Inf[:count hours',
|
||||
'a_hour' => '{1}an hour|{0}:count hours|]1,Inf[:count hours',
|
||||
'hour' => '{1}:count hour|{0}:count hours|[-Inf,Inf]:count hours',
|
||||
'a_hour' => '{1}an hour|{0}:count hours|[-Inf,Inf]:count hours',
|
||||
'h' => ':counth',
|
||||
'minute' => '{1}:count minute|{0}:count minutes|]1,Inf[:count minutes',
|
||||
'a_minute' => '{1}a minute|{0}:count minutes|]1,Inf[:count minutes',
|
||||
'minute' => '{1}:count minute|{0}:count minutes|[-Inf,Inf]:count minutes',
|
||||
'a_minute' => '{1}a minute|{0}:count minutes|[-Inf,Inf]:count minutes',
|
||||
'min' => ':countm',
|
||||
'second' => '{1}:count second|{0}:count seconds|]1,Inf[:count seconds',
|
||||
'a_second' => '{1}a few seconds|{0}:count seconds|]1,Inf[:count seconds',
|
||||
'second' => '{1}:count second|{0}:count seconds|[-Inf,Inf]:count seconds',
|
||||
'a_second' => '{0,1}a few seconds|[-Inf,Inf]:count seconds',
|
||||
's' => ':counts',
|
||||
'millisecond' => '{1}:count millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds',
|
||||
'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds',
|
||||
'millisecond' => '{1}:count millisecond|{0}:count milliseconds|[-Inf,Inf]:count milliseconds',
|
||||
'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|[-Inf,Inf]:count milliseconds',
|
||||
'ms' => ':countms',
|
||||
'microsecond' => '{1}:count microsecond|{0}:count microseconds|]1,Inf[:count microseconds',
|
||||
'a_microsecond' => '{1}a microsecond|{0}:count microseconds|]1,Inf[:count microseconds',
|
||||
'microsecond' => '{1}:count microsecond|{0}:count microseconds|[-Inf,Inf]:count microseconds',
|
||||
'a_microsecond' => '{1}a microsecond|{0}:count microseconds|[-Inf,Inf]:count microseconds',
|
||||
'µs' => ':countµs',
|
||||
'ago' => ':time ago',
|
||||
'from_now' => ':time from now',
|
||||
|
@ -59,7 +59,7 @@ return [
|
|||
'diff_tomorrow' => 'tomorrow',
|
||||
'diff_before_yesterday' => 'before yesterday',
|
||||
'diff_after_tomorrow' => 'after tomorrow',
|
||||
'period_recurrences' => '{1}once|{0}:count times|]1,Inf[:count times',
|
||||
'period_recurrences' => '{1}once|{0}:count times|[-Inf,Inf]:count times',
|
||||
'period_interval' => 'every :interval',
|
||||
'period_start_date' => 'from :date',
|
||||
'period_end_date' => 'to :date',
|
||||
|
|
|
@ -21,25 +21,25 @@
|
|||
*/
|
||||
return [
|
||||
'year' => ':count tahun',
|
||||
'a_year' => '{1}setahun|]1,Inf[:count tahun',
|
||||
'a_year' => '{1}setahun|[-Inf,Inf]:count tahun',
|
||||
'y' => ':countthn',
|
||||
'month' => ':count bulan',
|
||||
'a_month' => '{1}sebulan|]1,Inf[:count bulan',
|
||||
'a_month' => '{1}sebulan|[-Inf,Inf]:count bulan',
|
||||
'm' => ':countbln',
|
||||
'week' => ':count minggu',
|
||||
'a_week' => '{1}seminggu|]1,Inf[:count minggu',
|
||||
'a_week' => '{1}seminggu|[-Inf,Inf]:count minggu',
|
||||
'w' => ':countmgg',
|
||||
'day' => ':count hari',
|
||||
'a_day' => '{1}sehari|]1,Inf[:count hari',
|
||||
'a_day' => '{1}sehari|[-Inf,Inf]:count hari',
|
||||
'd' => ':counthr',
|
||||
'hour' => ':count jam',
|
||||
'a_hour' => '{1}sejam|]1,Inf[:count jam',
|
||||
'a_hour' => '{1}sejam|[-Inf,Inf]:count jam',
|
||||
'h' => ':countj',
|
||||
'minute' => ':count menit',
|
||||
'a_minute' => '{1}semenit|]1,Inf[:count menit',
|
||||
'a_minute' => '{1}semenit|[-Inf,Inf]:count menit',
|
||||
'min' => ':countmnt',
|
||||
'second' => ':count detik',
|
||||
'a_second' => '{1}beberapa detik|]1,Inf[:count detik',
|
||||
'a_second' => '{1}beberapa detik|[-Inf,Inf]:count detik',
|
||||
's' => ':countdt',
|
||||
'ago' => ':time yang lalu',
|
||||
'from_now' => ':time dari sekarang',
|
||||
|
|
|
@ -38,7 +38,7 @@ return [
|
|||
'minute' => ':count分',
|
||||
'min' => ':count分',
|
||||
'second' => ':count秒',
|
||||
'a_second' => '{1}数秒|]1,Inf[:count秒',
|
||||
'a_second' => '{1}数秒|[-Inf,Inf]:count秒',
|
||||
's' => ':count秒',
|
||||
'ago' => ':time前',
|
||||
'from_now' => ':time後',
|
||||
|
|
|
@ -16,13 +16,20 @@
|
|||
* - JD Isaacks
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}setaun|]1,Inf[:count taun',
|
||||
'month' => '{1}sewulan|]1,Inf[:count wulan',
|
||||
'week' => '{1}sakminggu|]1,Inf[:count minggu',
|
||||
'day' => '{1}sedinten|]1,Inf[:count dinten',
|
||||
'hour' => '{1}setunggal jam|]1,Inf[:count jam',
|
||||
'minute' => '{1}setunggal menit|]1,Inf[:count menit',
|
||||
'second' => '{1}sawetawis detik|]1,Inf[:count detik',
|
||||
'year' => ':count taun',
|
||||
'a_year' => '{1}setaun|[-Inf,Inf]:count taun',
|
||||
'month' => ':count wulan',
|
||||
'a_month' => '{1}sewulan|[-Inf,Inf]:count wulan',
|
||||
'week' => ':count minggu',
|
||||
'a_week' => '{1}sakminggu|[-Inf,Inf]:count minggu',
|
||||
'day' => ':count dina',
|
||||
'a_day' => '{1}sedina|[-Inf,Inf]:count dina',
|
||||
'hour' => ':count jam',
|
||||
'a_hour' => '{1}setunggal jam|[-Inf,Inf]:count jam',
|
||||
'minute' => ':count menit',
|
||||
'a_minute' => '{1}setunggal menit|[-Inf,Inf]:count menit',
|
||||
'second' => ':count detik',
|
||||
'a_second' => '{0,1}sawetawis detik|[-Inf,Inf]:count detik',
|
||||
'ago' => ':time ingkang kepengker',
|
||||
'from_now' => 'wonten ing :time',
|
||||
'diff_today' => 'Dinten',
|
||||
|
|
|
@ -30,25 +30,25 @@ use Carbon\CarbonInterface;
|
|||
return [
|
||||
'year' => ':count წელი',
|
||||
'y' => ':count წელი',
|
||||
'a_year' => '{1}წელი|]1,Inf[:count წელი',
|
||||
'a_year' => '{1}წელი|[-Inf,Inf]:count წელი',
|
||||
'month' => ':count თვე',
|
||||
'm' => ':count თვე',
|
||||
'a_month' => '{1}თვე|]1,Inf[:count თვე',
|
||||
'a_month' => '{1}თვე|[-Inf,Inf]:count თვე',
|
||||
'week' => ':count კვირა',
|
||||
'w' => ':count კვირა',
|
||||
'a_week' => '{1}კვირა|]1,Inf[:count კვირა',
|
||||
'a_week' => '{1}კვირა|[-Inf,Inf]:count კვირა',
|
||||
'day' => ':count დღე',
|
||||
'd' => ':count დღე',
|
||||
'a_day' => '{1}დღე|]1,Inf[:count დღე',
|
||||
'a_day' => '{1}დღე|[-Inf,Inf]:count დღე',
|
||||
'hour' => ':count საათი',
|
||||
'h' => ':count საათი',
|
||||
'a_hour' => '{1}საათი|]1,Inf[:count საათი',
|
||||
'a_hour' => '{1}საათი|[-Inf,Inf]:count საათი',
|
||||
'minute' => ':count წუთი',
|
||||
'min' => ':count წუთი',
|
||||
'a_minute' => '{1}წუთი|]1,Inf[:count წუთი',
|
||||
'a_minute' => '{1}წუთი|[-Inf,Inf]:count წუთი',
|
||||
'second' => ':count წამი',
|
||||
's' => ':count წამი',
|
||||
'a_second' => '{1}რამდენიმე წამი|]1,Inf[:count წამი',
|
||||
'a_second' => '{1}რამდენიმე წამი|[-Inf,Inf]:count წამი',
|
||||
'ago' => static function ($time) {
|
||||
$replacements = [
|
||||
// year
|
||||
|
|
|
@ -31,32 +31,32 @@ return array_replace_recursive(require __DIR__.'/en.php', [
|
|||
'first_day_of_week' => 1,
|
||||
'day_of_first_week_of_year' => 1,
|
||||
|
||||
'year' => '{1}ukioq :count|{0}:count ukiut|]1,Inf[ukiut :count',
|
||||
'a_year' => '{1}ukioq|{0}:count ukiut|]1,Inf[ukiut :count',
|
||||
'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs',
|
||||
'year' => '{1}ukioq :count|{0}:count ukiut|[-Inf,Inf]ukiut :count',
|
||||
'a_year' => '{1}ukioq|{0}:count ukiut|[-Inf,Inf]ukiut :count',
|
||||
'y' => '{1}:countyr|{0}:countyrs|[-Inf,Inf]:countyrs',
|
||||
|
||||
'month' => '{1}qaammat :count|{0}:count qaammatit|]1,Inf[qaammatit :count',
|
||||
'a_month' => '{1}qaammat|{0}:count qaammatit|]1,Inf[qaammatit :count',
|
||||
'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos',
|
||||
'month' => '{1}qaammat :count|{0}:count qaammatit|[-Inf,Inf]qaammatit :count',
|
||||
'a_month' => '{1}qaammat|{0}:count qaammatit|[-Inf,Inf]qaammatit :count',
|
||||
'm' => '{1}:countmo|{0}:countmos|[-Inf,Inf]:countmos',
|
||||
|
||||
'week' => '{1}:count sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.',
|
||||
'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.',
|
||||
'week' => '{1}:count sap. ak.|{0}:count sap. ak.|[-Inf,Inf]:count sap. ak.',
|
||||
'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|[-Inf,Inf]:count sap. ak.',
|
||||
'w' => ':countw',
|
||||
|
||||
'day' => '{1}:count ulloq|{0}:count ullut|]1,Inf[:count ullut',
|
||||
'a_day' => '{1}a ulloq|{0}:count ullut|]1,Inf[:count ullut',
|
||||
'day' => '{1}:count ulloq|{0}:count ullut|[-Inf,Inf]:count ullut',
|
||||
'a_day' => '{1}a ulloq|{0}:count ullut|[-Inf,Inf]:count ullut',
|
||||
'd' => ':countd',
|
||||
|
||||
'hour' => '{1}:count tiimi|{0}:count tiimit|]1,Inf[:count tiimit',
|
||||
'a_hour' => '{1}tiimi|{0}:count tiimit|]1,Inf[:count tiimit',
|
||||
'hour' => '{1}:count tiimi|{0}:count tiimit|[-Inf,Inf]:count tiimit',
|
||||
'a_hour' => '{1}tiimi|{0}:count tiimit|[-Inf,Inf]:count tiimit',
|
||||
'h' => ':counth',
|
||||
|
||||
'minute' => '{1}:count minutsi|{0}:count minutsit|]1,Inf[:count minutsit',
|
||||
'a_minute' => '{1}a minutsi|{0}:count minutsit|]1,Inf[:count minutsit',
|
||||
'minute' => '{1}:count minutsi|{0}:count minutsit|[-Inf,Inf]:count minutsit',
|
||||
'a_minute' => '{1}a minutsi|{0}:count minutsit|[-Inf,Inf]:count minutsit',
|
||||
'min' => ':countm',
|
||||
|
||||
'second' => '{1}:count sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit',
|
||||
'a_second' => '{1}sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit',
|
||||
'second' => '{1}:count sikunti|{0}:count sikuntit|[-Inf,Inf]:count sikuntit',
|
||||
'a_second' => '{1}sikunti|{0}:count sikuntit|[-Inf,Inf]:count sikuntit',
|
||||
's' => ':counts',
|
||||
|
||||
'ago' => ':time matuma siorna',
|
||||
|
|
|
@ -17,19 +17,25 @@
|
|||
* - Sovichet Tep
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}មួយឆ្នាំ|]1,Inf[:count ឆ្នាំ',
|
||||
'year' => ':count ឆ្នាំ',
|
||||
'a_year' => '{1}មួយឆ្នាំ|[-Inf,Inf]:count ឆ្នាំ',
|
||||
'y' => ':count ឆ្នាំ',
|
||||
'month' => '{1}មួយខែ|]1,Inf[:count ខែ',
|
||||
'month' => ':count ខែ',
|
||||
'a_month' => '{1}មួយខែ|[-Inf,Inf]:count ខែ',
|
||||
'm' => ':count ខែ',
|
||||
'week' => ':count សប្ដាហ៍',
|
||||
'w' => ':count សប្ដាហ៍',
|
||||
'day' => '{1}មួយថ្ងៃ|]1,Inf[:count ថ្ងៃ',
|
||||
'week' => ':count សប្តាហ៍',
|
||||
'w' => ':count សប្តាហ៍',
|
||||
'day' => ':count ថ្ងៃ',
|
||||
'a_day' => '{1}មួយថ្ងៃ|[-Inf,Inf]:count ថ្ងៃ',
|
||||
'd' => ':count ថ្ងៃ',
|
||||
'hour' => '{1}មួយម៉ោង|]1,Inf[:count ម៉ោង',
|
||||
'hour' => ':count ម៉ោង',
|
||||
'a_hour' => '{1}មួយម៉ោង|[-Inf,Inf]:count ម៉ោង',
|
||||
'h' => ':count ម៉ោង',
|
||||
'minute' => '{1}មួយនាទី|]1,Inf[:count នាទី',
|
||||
'minute' => ':count នាទី',
|
||||
'a_minute' => '{1}មួយនាទី|[-Inf,Inf]:count នាទី',
|
||||
'min' => ':count នាទី',
|
||||
'second' => '{1}ប៉ុន្មានវិនាទី|]1,Inf[:count វិនាទី',
|
||||
'second' => ':count វិនាទី',
|
||||
'a_second' => '{0,1}ប៉ុន្មានវិនាទី|[-Inf,Inf]:count វិនាទី',
|
||||
's' => ':count វិនាទី',
|
||||
'ago' => ':timeមុន',
|
||||
'from_now' => ':timeទៀត',
|
||||
|
|
|
@ -17,13 +17,20 @@
|
|||
* - rajeevnaikte
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}ಒಂದು ವರ್ಷ|]1,Inf[:count ವರ್ಷ',
|
||||
'month' => '{1}ಒಂದು ತಿಂಗಳು|]1,Inf[:count ತಿಂಗಳು',
|
||||
'week' => '{1}ಒಂದು ವಾರ|]1,Inf[:count ವಾರಗಳು',
|
||||
'day' => '{1}ಒಂದು ದಿನ|]1,Inf[:count ದಿನ',
|
||||
'hour' => '{1}ಒಂದು ಗಂಟೆ|]1,Inf[:count ಗಂಟೆ',
|
||||
'minute' => '{1}ಒಂದು ನಿಮಿಷ|]1,Inf[:count ನಿಮಿಷ',
|
||||
'second' => '{1}ಕೆಲವು ಕ್ಷಣಗಳು|]1,Inf[:count ಸೆಕೆಂಡುಗಳು',
|
||||
'year' => '{1}:count ವರ್ಷ|[-Inf,Inf]:count ವರ್ಷಗಳು',
|
||||
'a_year' => '{1}ಒಂದು ವರ್ಷ|[-Inf,Inf]:count ವರ್ಷಗಳು',
|
||||
'month' => ':count ತಿಂಗಳು',
|
||||
'a_month' => '{1}ಒಂದು ತಿಂಗಳು|[-Inf,Inf]:count ತಿಂಗಳು',
|
||||
'week' => '{1}:count ವಾರ|[-Inf,Inf]:count ವಾರಗಳು',
|
||||
'a_week' => '{1}ಒಂದು ವಾರ|[-Inf,Inf]:count ವಾರಗಳು',
|
||||
'day' => '{1}:count ದಿನ|[-Inf,Inf]:count ದಿನಗಳು',
|
||||
'a_day' => '{1}ಒಂದು ದಿನ|[-Inf,Inf]:count ದಿನಗಳು',
|
||||
'hour' => '{1}:count ಗಂಟೆ|[-Inf,Inf]:count ಗಂಟೆಗಳು',
|
||||
'a_hour' => '{1}ಒಂದು ಗಂಟೆ|[-Inf,Inf]:count ಗಂಟೆಗಳು',
|
||||
'minute' => '{1}:count ನಿಮಿಷ|[-Inf,Inf]:count ನಿಮಿಷಗಳು',
|
||||
'a_minute' => '{1}ಒಂದು ನಿಮಿಷ|[-Inf,Inf]:count ನಿಮಿಷಗಳು',
|
||||
'second' => '{0,1}:count ಸೆಕೆಂಡ್|[-Inf,Inf]:count ಸೆಕೆಂಡುಗಳು',
|
||||
'a_second' => '{0,1}ಕೆಲವು ಕ್ಷಣಗಳು|[-Inf,Inf]:count ಸೆಕೆಂಡುಗಳು',
|
||||
'ago' => ':time ಹಿಂದೆ',
|
||||
'from_now' => ':time ನಂತರ',
|
||||
'diff_now' => 'ಈಗ',
|
||||
|
|
|
@ -22,25 +22,25 @@
|
|||
*/
|
||||
return [
|
||||
'year' => ':count년',
|
||||
'a_year' => '{1}일년|]1,Inf[:count년',
|
||||
'a_year' => '{1}일년|[-Inf,Inf]:count년',
|
||||
'y' => ':count년',
|
||||
'month' => ':count개월',
|
||||
'a_month' => '{1}한달|]1,Inf[:count개월',
|
||||
'a_month' => '{1}한달|[-Inf,Inf]:count개월',
|
||||
'm' => ':count개월',
|
||||
'week' => ':count주',
|
||||
'a_week' => '{1}일주일|]1,Inf[:count 주',
|
||||
'a_week' => '{1}일주일|[-Inf,Inf]:count 주',
|
||||
'w' => ':count주일',
|
||||
'day' => ':count일',
|
||||
'a_day' => '{1}하루|]1,Inf[:count일',
|
||||
'a_day' => '{1}하루|[-Inf,Inf]:count일',
|
||||
'd' => ':count일',
|
||||
'hour' => ':count시간',
|
||||
'a_hour' => '{1}한시간|]1,Inf[:count시간',
|
||||
'a_hour' => '{1}한시간|[-Inf,Inf]:count시간',
|
||||
'h' => ':count시간',
|
||||
'minute' => ':count분',
|
||||
'a_minute' => '{1}일분|]1,Inf[:count분',
|
||||
'a_minute' => '{1}일분|[-Inf,Inf]:count분',
|
||||
'min' => ':count분',
|
||||
'second' => ':count초',
|
||||
'a_second' => '{1}몇초|]1,Inf[:count초',
|
||||
'a_second' => '{1}몇초|[-Inf,Inf]:count초',
|
||||
's' => ':count초',
|
||||
'ago' => ':time 전',
|
||||
'from_now' => ':time 후',
|
||||
|
|
|
@ -27,7 +27,8 @@ return [
|
|||
'h' => ':count ຊມ. ',
|
||||
'minute' => ':count ນາທີ',
|
||||
'min' => ':count ນທ. ',
|
||||
'second' => '{1}ບໍ່ເທົ່າໃດວິນາທີ|]1,Inf[:count ວິນາທີ',
|
||||
'second' => ':count ວິນາທີ',
|
||||
'a_second' => '{0,1}ບໍ່ເທົ່າໃດວິນາທີ|[-Inf,Inf]:count ວິນາທີ',
|
||||
's' => ':count ວິ. ',
|
||||
'ago' => ':timeຜ່ານມາ',
|
||||
'from_now' => 'ອີກ :time',
|
||||
|
|
|
@ -21,29 +21,29 @@
|
|||
*/
|
||||
return [
|
||||
'year' => ':count tahun',
|
||||
'a_year' => '{1}setahun|]1,Inf[:count tahun',
|
||||
'a_year' => '{1}setahun|[-Inf,Inf]:count tahun',
|
||||
'y' => ':count tahun',
|
||||
'month' => ':count bulan',
|
||||
'a_month' => '{1}sebulan|]1,Inf[:count bulan',
|
||||
'a_month' => '{1}sebulan|[-Inf,Inf]:count bulan',
|
||||
'm' => ':count bulan',
|
||||
'week' => ':count minggu',
|
||||
'a_week' => '{1}seminggu|]1,Inf[:count minggu',
|
||||
'a_week' => '{1}seminggu|[-Inf,Inf]:count minggu',
|
||||
'w' => ':count minggu',
|
||||
'day' => ':count hari',
|
||||
'a_day' => '{1}sehari|]1,Inf[:count hari',
|
||||
'a_day' => '{1}sehari|[-Inf,Inf]:count hari',
|
||||
'd' => ':count hari',
|
||||
'hour' => ':count jam',
|
||||
'a_hour' => '{1}sejam|]1,Inf[:count jam',
|
||||
'a_hour' => '{1}sejam|[-Inf,Inf]:count jam',
|
||||
'h' => ':count jam',
|
||||
'minute' => ':count minit',
|
||||
'a_minute' => '{1}seminit|]1,Inf[:count minit',
|
||||
'a_minute' => '{1}seminit|[-Inf,Inf]:count minit',
|
||||
'min' => ':count minit',
|
||||
'second' => ':count saat',
|
||||
'a_second' => '{1}beberapa saat|]1,Inf[:count saat',
|
||||
'a_second' => '{1}beberapa saat|[-Inf,Inf]:count saat',
|
||||
'millisecond' => ':count milisaat',
|
||||
'a_millisecond' => '{1}semilisaat|]1,Inf[:count milliseconds',
|
||||
'a_millisecond' => '{1}semilisaat|[-Inf,Inf]:count milliseconds',
|
||||
'microsecond' => ':count mikrodetik',
|
||||
'a_microsecond' => '{1}semikrodetik|]1,Inf[:count mikrodetik',
|
||||
'a_microsecond' => '{1}semikrodetik|[-Inf,Inf]:count mikrodetik',
|
||||
's' => ':count saat',
|
||||
'ago' => ':time yang lepas',
|
||||
'from_now' => ':time dari sekarang',
|
||||
|
|
|
@ -16,19 +16,25 @@
|
|||
* - Nay Lin Aung
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}တစ်နှစ်|]1,Inf[:count နှစ်',
|
||||
'year' => ':count နှစ်',
|
||||
'a_year' => '{1}တစ်နှစ်|[-Inf,Inf]:count နှစ်',
|
||||
'y' => ':count နှစ်',
|
||||
'month' => '{1}တစ်လ|]1,Inf[:count လ',
|
||||
'month' => ':count လ',
|
||||
'a_month' => '{1}တစ်လ|[-Inf,Inf]:count လ',
|
||||
'm' => ':count လ',
|
||||
'week' => ':count ပတ်',
|
||||
'w' => ':count ပတ်',
|
||||
'day' => '{1}တစ်ရက်|]1,Inf[:count ရက်',
|
||||
'day' => ':count ရက်',
|
||||
'a_day' => '{1}တစ်ရက်|[-Inf,Inf]:count ရက်',
|
||||
'd' => ':count ရက်',
|
||||
'hour' => '{1}တစ်နာရီ|]1,Inf[:count နာရီ',
|
||||
'hour' => ':count နာရီ',
|
||||
'a_hour' => '{1}တစ်နာရီ|[-Inf,Inf]:count နာရီ',
|
||||
'h' => ':count နာရီ',
|
||||
'minute' => '{1}တစ်မိနစ်|]1,Inf[:count မိနစ်',
|
||||
'minute' => ':count မိနစ်',
|
||||
'a_minute' => '{1}တစ်မိနစ်|[-Inf,Inf]:count မိနစ်',
|
||||
'min' => ':count မိနစ်',
|
||||
'second' => '{1}စက္ကန်.အနည်းငယ်|]1,Inf[:count စက္ကန့်',
|
||||
'second' => ':count စက္ကန့်',
|
||||
'a_second' => '{0,1}စက္ကန်.အနည်းငယ်|[-Inf,Inf]:count စက္ကန့်',
|
||||
's' => ':count စက္ကန့်',
|
||||
'ago' => 'လွန်ခဲ့သော :time က',
|
||||
'from_now' => 'လာမည့် :time မှာ',
|
||||
|
|
|
@ -38,16 +38,22 @@ $weekdays = [
|
|||
* Authors:
|
||||
* - Narain Sagar
|
||||
* - Sawood Alam
|
||||
* - Narain Sagar
|
||||
*/
|
||||
return [
|
||||
'year' => '{1}'.'هڪ سال'.'|:count '.'سال',
|
||||
'month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا',
|
||||
'week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا',
|
||||
'day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن',
|
||||
'hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ',
|
||||
'minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ',
|
||||
'second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ',
|
||||
'year' => ':count '.'سال',
|
||||
'a_year' => '{1}'.'هڪ سال'.'|:count '.'سال',
|
||||
'month' => ':count '.'مهينا',
|
||||
'a_month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا',
|
||||
'week' => ':count '.'هفتا',
|
||||
'a_week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا',
|
||||
'day' => ':count '.'ڏينهن',
|
||||
'a_day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن',
|
||||
'hour' => ':count '.'ڪلاڪ',
|
||||
'a_hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ',
|
||||
'minute' => ':count '.'منٽ',
|
||||
'a_minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ',
|
||||
'second' => ':count '.'سيڪنڊ',
|
||||
'a_second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ',
|
||||
'ago' => ':time اڳ',
|
||||
'from_now' => ':time پوء',
|
||||
'diff_yesterday' => 'ڪالهه',
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
return array_replace_recursive(require __DIR__.'/en.php', [
|
||||
'year' => ':count sanad|:count sanadood',
|
||||
'a_year' => 'sanad|:count sanadood',
|
||||
'y' => '{1}:countsn|{0}:countsns|]1,Inf[:countsn',
|
||||
'y' => '{1}:countsn|{0}:countsns|[-Inf,Inf]:countsn',
|
||||
'month' => ':count bil|:count bilood',
|
||||
'a_month' => 'bil|:count bilood',
|
||||
'm' => ':countbil',
|
||||
|
|
|
@ -26,7 +26,7 @@ return [
|
|||
'year' => ':count godina|:count godine|:count godina',
|
||||
'y' => ':count g.',
|
||||
'month' => ':count mesec|:count meseca|:count meseci',
|
||||
'm' => ':count mj.',
|
||||
'm' => ':count mes.',
|
||||
'week' => ':count nedelja|:count nedelje|:count nedelja',
|
||||
'w' => ':count ned.',
|
||||
'day' => ':count dan|:count dana|:count dana',
|
||||
|
|
|
@ -34,7 +34,7 @@ return [
|
|||
'minute' => ':count นาที',
|
||||
'min' => ':count นาที',
|
||||
'second' => ':count วินาที',
|
||||
'a_second' => '{1}ไม่กี่วินาที|]1,Inf[:count วินาที',
|
||||
'a_second' => '{1}ไม่กี่วินาที|[-Inf,Inf]:count วินาที',
|
||||
's' => ':count วินาที',
|
||||
'ago' => ':timeที่แล้ว',
|
||||
'from_now' => 'อีก :time',
|
||||
|
|
|
@ -23,25 +23,25 @@
|
|||
*/
|
||||
return [
|
||||
'year' => ':count yıl',
|
||||
'a_year' => '{1}bir yıl|]1,Inf[:count yıl',
|
||||
'a_year' => '{1}bir yıl|[-Inf,Inf]:count yıl',
|
||||
'y' => ':county',
|
||||
'month' => ':count ay',
|
||||
'a_month' => '{1}bir ay|]1,Inf[:count ay',
|
||||
'a_month' => '{1}bir ay|[-Inf,Inf]:count ay',
|
||||
'm' => ':countay',
|
||||
'week' => ':count hafta',
|
||||
'a_week' => '{1}bir hafta|]1,Inf[:count hafta',
|
||||
'a_week' => '{1}bir hafta|[-Inf,Inf]:count hafta',
|
||||
'w' => ':counth',
|
||||
'day' => ':count gün',
|
||||
'a_day' => '{1}bir gün|]1,Inf[:count gün',
|
||||
'a_day' => '{1}bir gün|[-Inf,Inf]:count gün',
|
||||
'd' => ':countg',
|
||||
'hour' => ':count saat',
|
||||
'a_hour' => '{1}bir saat|]1,Inf[:count saat',
|
||||
'a_hour' => '{1}bir saat|[-Inf,Inf]:count saat',
|
||||
'h' => ':countsa',
|
||||
'minute' => ':count dakika',
|
||||
'a_minute' => '{1}bir dakika|]1,Inf[:count dakika',
|
||||
'a_minute' => '{1}bir dakika|[-Inf,Inf]:count dakika',
|
||||
'min' => ':countdk',
|
||||
'second' => ':count saniye',
|
||||
'a_second' => '{1}birkaç saniye|]1,Inf[:count saniye',
|
||||
'a_second' => '{1}birkaç saniye|[-Inf,Inf]:count saniye',
|
||||
's' => ':countsn',
|
||||
'ago' => ':time önce',
|
||||
'from_now' => ':time sonra',
|
||||
|
|
|
@ -46,15 +46,22 @@ $weekdays = [
|
|||
* - hafezdivandari
|
||||
* - Hossein Jabbari
|
||||
* - nimamo
|
||||
* - Usman Zahid
|
||||
*/
|
||||
return [
|
||||
'year' => 'ایک سال|:count سال',
|
||||
'month' => 'ایک ماہ|:count ماہ',
|
||||
'week' => ':count ہفتے',
|
||||
'day' => 'ایک دن|:count دن',
|
||||
'hour' => 'ایک گھنٹہ|:count گھنٹے',
|
||||
'minute' => 'ایک منٹ|:count منٹ',
|
||||
'second' => 'چند سیکنڈ|:count سیکنڈ',
|
||||
'year' => ':count '.'سال',
|
||||
'a_year' => 'ایک سال|:count سال',
|
||||
'month' => ':count '.'ماہ',
|
||||
'a_month' => 'ایک ماہ|:count ماہ',
|
||||
'week' => ':count '.'ہفتے',
|
||||
'day' => ':count '.'دن',
|
||||
'a_day' => 'ایک دن|:count دن',
|
||||
'hour' => ':count '.'گھنٹے',
|
||||
'a_hour' => 'ایک گھنٹہ|:count گھنٹے',
|
||||
'minute' => ':count '.'منٹ',
|
||||
'a_minute' => 'ایک منٹ|:count منٹ',
|
||||
'second' => ':count '.'سیکنڈ',
|
||||
'a_second' => 'چند سیکنڈ|:count سیکنڈ',
|
||||
'ago' => ':time قبل',
|
||||
'from_now' => ':time بعد',
|
||||
'after' => ':time بعد',
|
||||
|
|
|
@ -37,7 +37,7 @@ return [
|
|||
'minute' => ':count:optional-space分钟',
|
||||
'min' => ':count:optional-space分钟',
|
||||
'second' => ':count:optional-space秒',
|
||||
'a_second' => '{1}几秒|]1,Inf[:count:optional-space秒',
|
||||
'a_second' => '{1}几秒|[-Inf,Inf]:count:optional-space秒',
|
||||
's' => ':count:optional-space秒',
|
||||
'ago' => ':time前',
|
||||
'from_now' => ':time后',
|
||||
|
|
|
@ -39,7 +39,7 @@ return [
|
|||
'minute' => ':count:optional-space分鐘',
|
||||
'min' => ':count:optional-space分鐘',
|
||||
'second' => ':count:optional-space秒',
|
||||
'a_second' => '{1}幾秒|]1,Inf[:count:optional-space秒',
|
||||
'a_second' => '{1}幾秒|[-Inf,Inf]:count:optional-space秒',
|
||||
's' => ':count:optional-space秒',
|
||||
'ago' => ':time前',
|
||||
'from_now' => ':time後',
|
||||
|
|
|
@ -319,7 +319,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null): ?self
|
||||
public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null): ?static
|
||||
{
|
||||
$month = self::monthToInt($month);
|
||||
|
||||
|
@ -405,7 +405,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null): ?self
|
||||
public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null): ?static
|
||||
{
|
||||
$month = self::monthToInt($month);
|
||||
$fields = static::getRangesByUnit();
|
||||
|
@ -563,7 +563,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function rawCreateFromFormat(string $format, string $time, $timezone = null): ?self
|
||||
public static function rawCreateFromFormat(string $format, string $time, $timezone = null): ?static
|
||||
{
|
||||
// Work-around for https://bugs.php.net/bug.php?id=80141
|
||||
$format = preg_replace('/(?<!\\\\)((?:\\\\{2})*)c/', '$1Y-m-d\TH:i:sP', $format);
|
||||
|
@ -640,7 +640,7 @@ trait Creator
|
|||
* @return static|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public static function createFromFormat($format, $time, $timezone = null): ?self
|
||||
public static function createFromFormat($format, $time, $timezone = null): ?static
|
||||
{
|
||||
$function = static::$createFromFormatFunction;
|
||||
|
||||
|
@ -685,9 +685,9 @@ trait Creator
|
|||
string $format,
|
||||
string $time,
|
||||
$timezone = null,
|
||||
?string $locale = self::DEFAULT_LOCALE,
|
||||
?string $locale = CarbonInterface::DEFAULT_LOCALE,
|
||||
?TranslatorInterface $translator = null
|
||||
): ?self {
|
||||
): ?static {
|
||||
$format = preg_replace_callback('/(?<!\\\\)(\\\\{2})*(LTS|LT|[Ll]{1,4})/', function ($match) use ($locale, $translator) {
|
||||
[$code] = $match;
|
||||
|
||||
|
@ -825,7 +825,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null): ?self
|
||||
public static function createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null): ?static
|
||||
{
|
||||
$format = preg_replace_callback(
|
||||
'/(?:\\\\[a-zA-Z]|[bfkqCEJKQRV]){2,}/',
|
||||
|
@ -855,7 +855,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null): ?self
|
||||
public static function createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null): ?static
|
||||
{
|
||||
$time = static::translateTimeString($time, $locale, static::DEFAULT_LOCALE, CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM);
|
||||
|
||||
|
@ -874,7 +874,7 @@ trait Creator
|
|||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function make($var, DateTimeZone|string|null $timezone = null): ?self
|
||||
public static function make($var, DateTimeZone|string|null $timezone = null): ?static
|
||||
{
|
||||
if ($var instanceof DateTimeInterface) {
|
||||
return static::instance($var);
|
||||
|
|
|
@ -1836,6 +1836,8 @@ trait Date
|
|||
|
||||
/**
|
||||
* Set the timezone or returns the timezone name if no arguments passed.
|
||||
*
|
||||
* @return ($value is null ? string : static)
|
||||
*/
|
||||
public function tz(DateTimeZone|string|int|null $value = null): static|string
|
||||
{
|
||||
|
|
|
@ -327,7 +327,7 @@ trait Localization
|
|||
}
|
||||
|
||||
/**
|
||||
* Translate a time string from the current locale (`$date->locale()`) to an other.
|
||||
* Translate a time string from the current locale (`$date->locale()`) to another one.
|
||||
*
|
||||
* @param string $timeString time string to translate
|
||||
* @param string|null $to output locale of the result returned ("en" by default)
|
||||
|
@ -659,7 +659,11 @@ trait Localization
|
|||
{
|
||||
$word = str_replace([':count', '%count', ':time'], '', $word);
|
||||
$word = strtr($word, ['’' => "'"]);
|
||||
$word = preg_replace('/({\d+(,(\d+|Inf))?}|[\[\]]\d+(,(\d+|Inf))?[\[\]])/', '', $word);
|
||||
$word = preg_replace(
|
||||
'/\{(?:-?\d+(?:\.\d+)?|-?Inf)(?:,(?:-?\d+|-?Inf))?}|[\[\]](?:-?\d+(?:\.\d+)?|-?Inf)(?:,(?:-?\d+|-?Inf))?[\[\]]/',
|
||||
'',
|
||||
$word,
|
||||
);
|
||||
|
||||
return trim($word);
|
||||
}
|
||||
|
@ -688,7 +692,7 @@ trait Localization
|
|||
|
||||
return $key === 'to'
|
||||
? self::cleanWordFromTranslationString(end($parts))
|
||||
: '(?:'.implode('|', array_map([static::class, 'cleanWordFromTranslationString'], $parts)).')';
|
||||
: '(?:'.implode('|', array_map(static::cleanWordFromTranslationString(...), $parts)).')';
|
||||
}, $keys);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue