Framework Update

This commit is contained in:
Enoch 2024-01-31 22:15:08 +08:00
parent b5ff5e8b5f
commit 20678a6a0c
1459 changed files with 25954 additions and 16153 deletions

View File

@ -10,7 +10,7 @@ use dnstools\ipv6; #用于IPv6相关
class DnsController class DnsController
{ {
public function DNS($type,$name,$rip,$id,$query) public function DNS($type,$name,$rip,$id,$query,$traffic=0)
{ {
#输出信息 #输出信息
#echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n"; #echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n";
@ -41,6 +41,10 @@ class DnsController
#此处无需修改 #此处无需修改
$send['id']=$id; $send['id']=$id;
$send['query']=$query; $send['query']=$query;
if(!isset($send['ttl'])){
$send['ttl']=0;
}
$send['info']=json_encode(['domain'=>$name,'querytype'=>$type,'answertype'=>$send['type'],'ip'=>$rip,'ttl'=>$send['ttl'],'detail'=>$send['detail']]);
$return=json_encode($send); $return=json_encode($send);
return $return; return $return;
} }

View File

@ -21,7 +21,11 @@ class IndexController
View::assign('userinfo', $request->session()->get('userinfo')); View::assign('userinfo', $request->session()->get('userinfo'));
return view('user'); return view('user');
} }
public function domain(Request $request)
{
View::assign('userinfo', $request->session()->get('userinfo'));
return view('index');
}
public function json(Request $request) public function json(Request $request)
{ {
return json(['code' => 0, 'msg' => 'ok']); return json(['code' => 0, 'msg' => 'ok']);

View File

@ -71,7 +71,12 @@
<div class="sidebar-heading"> <div class="sidebar-heading">
DNS解析 DNS解析
</div> </div>
<li class="nav-item">
<a class="nav-link" href="domain" >
<i class="fas fa-fw fa-globe"></i>
<span>域名列表</span>
</a>
</li>
<!-- Nav Item - Pages Collapse Menu --> <!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item"> <li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo" <a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo"
@ -118,8 +123,8 @@
<!-- Nav Item - Pages Collapse Menu --> <!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="user" > <a class="nav-link" href="user" >
<i class="fas fa-fw fa-folder"></i> <i class="fas fa-fw fa-user"></i>
<span>账号</span> <span>用户账号</span>
</a> </a>
</li> </li>

View File

@ -36,7 +36,7 @@
"yzh52521/webman-throttle": "^1.0", "yzh52521/webman-throttle": "^1.0",
"workerman/validation": "^3.0", "workerman/validation": "^3.0",
"yzh52521/easyhttp": "^1.0", "yzh52521/easyhttp": "^1.0",
"laysense/dns": "^0.0.4" "laysense/dns": "^0.1.0"
}, },
"suggest": { "suggest": {
"ext-event": "For better performance. " "ext-event": "For better performance. "

928
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@ use Webman\Route;
Route::group('/', function () { Route::group('/', function () {
Route::any('',[app\controller\IndexController::class, 'index']); Route::any('',[app\controller\IndexController::class, 'index']);
Route::any('user',[app\controller\IndexController::class, 'user']);
Route::any('domain',[app\controller\IndexController::class, 'domain']);
})->middleware([ })->middleware([
#app\middleware\Throttle::class, #app\middleware\Throttle::class,
app\middleware\Auth::class, app\middleware\Auth::class,

View File

@ -17,7 +17,7 @@ class DnsProcess
#echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n"; #echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n";
$dns=new DnsController; $dns=new DnsController;
$return=$dns->DNS($type,$name,$rip,$data->id,$data->query); $return=$dns->DNS($type,$name,$rip,$data->id,$data->query,$data->traffic);
$connection->send($return); $connection->send($return);
} }

View File

@ -16,8 +16,8 @@ use Dotenv\Dotenv;
use support\Log; use support\Log;
use Webman\Bootstrap; use Webman\Bootstrap;
use Webman\Config; use Webman\Config;
use Webman\Route;
use Webman\Middleware; use Webman\Middleware;
use Webman\Route;
use Webman\Util; use Webman\Util;
$worker = $worker ?? null; $worker = $worker ?? null;
@ -29,18 +29,18 @@ set_error_handler(function ($level, $message, $file = '', $line = 0) {
}); });
if ($worker) { if ($worker) {
register_shutdown_function(function ($start_time) { register_shutdown_function(function ($startTime) {
if (time() - $start_time <= 1) { if (time() - $startTime <= 0.1) {
sleep(1); sleep(1);
} }
}, time()); }, time());
} }
if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) { if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) { if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
Dotenv::createUnsafeImmutable(base_path())->load(); Dotenv::createUnsafeMutable(base_path(false))->load();
} else { } else {
Dotenv::createMutable(base_path())->load(); Dotenv::createMutable(base_path(false))->load();
} }
} }
@ -67,30 +67,30 @@ foreach (config('plugin', []) as $firm => $projects) {
} }
} }
Middleware::load(config('middleware', []), ''); Middleware::load(config('middleware', []));
foreach (config('plugin', []) as $firm => $projects) { foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) { foreach ($projects as $name => $project) {
if (!is_array($project) || $name === 'static') { if (!is_array($project) || $name === 'static') {
continue; continue;
} }
Middleware::load($project['middleware'] ?? [], ''); Middleware::load($project['middleware'] ?? []);
} }
Middleware::load($projects['middleware'] ?? [], $firm); Middleware::load($projects['middleware'] ?? [], $firm);
if ($static_middlewares = config("plugin.$firm.static.middleware")) { if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
Middleware::load(['__static__' => $static_middlewares], $firm); Middleware::load(['__static__' => $staticMiddlewares], $firm);
} }
} }
Middleware::load(['__static__' => config('static.middleware', [])], ''); Middleware::load(['__static__' => config('static.middleware', [])]);
foreach (config('bootstrap', []) as $class_name) { foreach (config('bootstrap', []) as $className) {
if (!class_exists($class_name)) { if (!class_exists($className)) {
$log = "Warning: Class $class_name setting in config/bootstrap.php not found\r\n"; $log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
echo $log; echo $log;
Log::error($log); Log::error($log);
continue; continue;
} }
/** @var Bootstrap $class_name */ /** @var Bootstrap $className */
$class_name::start($worker); $className::start($worker);
} }
foreach (config('plugin', []) as $firm => $projects) { foreach (config('plugin', []) as $firm => $projects) {
@ -98,26 +98,27 @@ foreach (config('plugin', []) as $firm => $projects) {
if (!is_array($project)) { if (!is_array($project)) {
continue; continue;
} }
foreach ($project['bootstrap'] ?? [] as $class_name) { foreach ($project['bootstrap'] ?? [] as $className) {
if (!class_exists($class_name)) { if (!class_exists($className)) {
$log = "Warning: Class $class_name setting in config/plugin/$firm/$name/bootstrap.php not found\r\n"; $log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
echo $log; echo $log;
Log::error($log); Log::error($log);
continue; continue;
} }
/** @var Bootstrap $class_name */ /** @var Bootstrap $className */
$class_name::start($worker); $className::start($worker);
} }
} }
foreach ($projects['bootstrap'] ?? [] as $class_name) { foreach ($projects['bootstrap'] ?? [] as $className) {
if (!class_exists($class_name)) { /** @var string $className */
$log = "Warning: Class $class_name setting in plugin/$firm/config/bootstrap.php not found\r\n"; if (!class_exists($className)) {
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
echo $log; echo $log;
Log::error($log); Log::error($log);
continue; continue;
} }
/** @var Bootstrap $class_name */ /** @var Bootstrap $className */
$class_name::start($worker); $className::start($worker);
} }
} }

View File

@ -17,17 +17,18 @@ use support\Container;
use support\Request; use support\Request;
use support\Response; use support\Response;
use support\Translation; use support\Translation;
use support\view\Raw;
use support\view\Blade; use support\view\Blade;
use support\view\Raw;
use support\view\ThinkPHP; use support\view\ThinkPHP;
use support\view\Twig; use support\view\Twig;
use Workerman\Worker; use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Webman\App; use Webman\App;
use Webman\Config; use Webman\Config;
use Webman\Route; use Webman\Route;
use Workerman\Protocols\Http\Session;
// Webman version use Workerman\Worker;
const WEBMAN_VERSION = '1.4';
// Project base path // Project base path
define('BASE_PATH', dirname(__DIR__)); define('BASE_PATH', dirname(__DIR__));
@ -39,11 +40,11 @@ define('BASE_PATH', dirname(__DIR__));
*/ */
function run_path(string $path = ''): string function run_path(string $path = ''): string
{ {
static $run_path = ''; static $runPath = '';
if (!$run_path) { if (!$runPath) {
$run_path = \is_phar() ? \dirname(\Phar::running(false)) : BASE_PATH; $runPath = is_phar() ? dirname(Phar::running(false)) : BASE_PATH;
} }
return \path_combine($run_path, $path); return path_combine($runPath, $path);
} }
/** /**
@ -54,9 +55,9 @@ function run_path(string $path = ''): string
function base_path($path = ''): string function base_path($path = ''): string
{ {
if (false === $path) { if (false === $path) {
return \run_path(); return run_path();
} }
return \path_combine(BASE_PATH, $path); return path_combine(BASE_PATH, $path);
} }
/** /**
@ -66,7 +67,7 @@ function base_path($path = ''): string
*/ */
function app_path(string $path = ''): string function app_path(string $path = ''): string
{ {
return \path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path); return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path);
} }
/** /**
@ -76,11 +77,11 @@ function app_path(string $path = ''): string
*/ */
function public_path(string $path = ''): string function public_path(string $path = ''): string
{ {
static $public_path = ''; static $publicPath = '';
if (!$public_path) { if (!$publicPath) {
$public_path = \config('app.public_path') ? : \run_path('public'); $publicPath = \config('app.public_path') ?: run_path('public');
} }
return \path_combine($public_path, $path); return path_combine($publicPath, $path);
} }
/** /**
@ -90,7 +91,7 @@ function public_path(string $path = ''): string
*/ */
function config_path(string $path = ''): string function config_path(string $path = ''): string
{ {
return \path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path); return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path);
} }
/** /**
@ -100,11 +101,11 @@ function config_path(string $path = ''): string
*/ */
function runtime_path(string $path = ''): string function runtime_path(string $path = ''): string
{ {
static $runtime_path = ''; static $runtimePath = '';
if (!$runtime_path) { if (!$runtimePath) {
$runtime_path = \config('app.runtime_path') ? : \run_path('runtime'); $runtimePath = \config('app.runtime_path') ?: run_path('runtime');
} }
return \path_combine($runtime_path, $path); return path_combine($runtimePath, $path);
} }
/** /**
@ -138,7 +139,7 @@ function response(string $body = '', int $status = 200, array $headers = []): Re
*/ */
function json($data, int $options = JSON_UNESCAPED_UNICODE): Response function json($data, int $options = JSON_UNESCAPED_UNICODE): Response
{ {
return new Response(200, ['Content-Type' => 'application/json'], \json_encode($data, $options)); return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
} }
/** /**
@ -157,15 +158,15 @@ function xml($xml): Response
/** /**
* Jsonp response * Jsonp response
* @param $data * @param $data
* @param string $callback_name * @param string $callbackName
* @return Response * @return Response
*/ */
function jsonp($data, string $callback_name = 'callback'): Response function jsonp($data, string $callbackName = 'callback'): Response
{ {
if (!\is_scalar($data) && null !== $data) { if (!is_scalar($data) && null !== $data) {
$data = \json_encode($data); $data = json_encode($data);
} }
return new Response(200, [], "$callback_name($data)"); return new Response(200, [], "$callbackName($data)");
} }
/** /**
@ -189,14 +190,15 @@ function redirect(string $location, int $status = 302, array $headers = []): Res
* @param string $template * @param string $template
* @param array $vars * @param array $vars
* @param string|null $app * @param string|null $app
* @param string|null $plugin
* @return Response * @return Response
*/ */
function view(string $template, array $vars = [], string $app = null): Response function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response
{ {
$request = \request(); $request = \request();
$plugin = $request->plugin ?? ''; $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler'); $handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
return new Response(200, [], $handler::render($template, $vars, $app)); return new Response(200, [], $handler::render($template, $vars, $app, $plugin));
} }
/** /**
@ -242,6 +244,9 @@ function think_view(string $template, array $vars = [], string $app = null): Res
* @param array $vars * @param array $vars
* @param string|null $app * @param string|null $app
* @return Response * @return Response
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/ */
function twig_view(string $template, array $vars = [], string $app = null): Response function twig_view(string $template, array $vars = [], string $app = null): Response
{ {
@ -285,8 +290,8 @@ function route(string $name, ...$parameters): string
return $route->url(); return $route->url();
} }
if (\is_array(\current($parameters))) { if (is_array(current($parameters))) {
$parameters = \current($parameters); $parameters = current($parameters);
} }
return $route->url($parameters); return $route->url($parameters);
@ -296,7 +301,7 @@ function route(string $name, ...$parameters): string
* Session * Session
* @param mixed $key * @param mixed $key
* @param mixed $default * @param mixed $default
* @return mixed * @return mixed|bool|Session
*/ */
function session($key = null, $default = null) function session($key = null, $default = null)
{ {
@ -304,14 +309,14 @@ function session($key = null, $default = null)
if (null === $key) { if (null === $key) {
return $session; return $session;
} }
if (\is_array($key)) { if (is_array($key)) {
$session->put($key); $session->put($key);
return null; return null;
} }
if (\strpos($key, '.')) { if (strpos($key, '.')) {
$key_array = \explode('.', $key); $keyArray = explode('.', $key);
$value = $session->all(); $value = $session->all();
foreach ($key_array as $index) { foreach ($keyArray as $index) {
if (!isset($value[$index])) { if (!isset($value[$index])) {
return $default; return $default;
} }
@ -339,7 +344,7 @@ function trans(string $id, array $parameters = [], string $domain = null, string
/** /**
* Locale * Locale
* @param string|null $locale * @param string|null $locale
* @return void * @return string
*/ */
function locale(string $locale = null): string function locale(string $locale = null): string
{ {
@ -347,6 +352,7 @@ function locale(string $locale = null): string
return Translation::getLocale(); return Translation::getLocale();
} }
Translation::setLocale($locale); Translation::setLocale($locale);
return $locale;
} }
/** /**
@ -355,7 +361,7 @@ function locale(string $locale = null): string
*/ */
function not_found(): Response function not_found(): Response
{ {
return new Response(404, [], \file_get_contents(public_path() . '/404.html')); return new Response(404, [], file_get_contents(public_path() . '/404.html'));
} }
/** /**
@ -367,18 +373,18 @@ function not_found(): Response
*/ */
function copy_dir(string $source, string $dest, bool $overwrite = false) function copy_dir(string $source, string $dest, bool $overwrite = false)
{ {
if (\is_dir($source)) { if (is_dir($source)) {
if (!is_dir($dest)) { if (!is_dir($dest)) {
\mkdir($dest); mkdir($dest);
} }
$files = \scandir($source); $files = scandir($source);
foreach ($files as $file) { foreach ($files as $file) {
if ($file !== "." && $file !== "..") { if ($file !== "." && $file !== "..") {
\copy_dir("$source/$file", "$dest/$file"); copy_dir("$source/$file", "$dest/$file", $overwrite);
} }
} }
} else if (\file_exists($source) && ($overwrite || !\file_exists($dest))) { } else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
\copy($source, $dest); copy($source, $dest);
} }
} }
@ -389,14 +395,14 @@ function copy_dir(string $source, string $dest, bool $overwrite = false)
*/ */
function remove_dir(string $dir): bool function remove_dir(string $dir): bool
{ {
if (\is_link($dir) || \is_file($dir)) { if (is_link($dir) || is_file($dir)) {
return \unlink($dir); return unlink($dir);
} }
$files = \array_diff(\scandir($dir), array('.', '..')); $files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $file) { foreach ($files as $file) {
(\is_dir("$dir/$file") && !\is_link($dir)) ? \remove_dir("$dir/$file") : \unlink("$dir/$file"); (is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
} }
return \rmdir($dir); return rmdir($dir);
} }
/** /**
@ -406,7 +412,7 @@ function remove_dir(string $dir): bool
*/ */
function worker_bind($worker, $class) function worker_bind($worker, $class)
{ {
$callback_map = [ $callbackMap = [
'onConnect', 'onConnect',
'onMessage', 'onMessage',
'onClose', 'onClose',
@ -414,28 +420,29 @@ function worker_bind($worker, $class)
'onBufferFull', 'onBufferFull',
'onBufferDrain', 'onBufferDrain',
'onWorkerStop', 'onWorkerStop',
'onWebSocketConnect' 'onWebSocketConnect',
'onWorkerReload'
]; ];
foreach ($callback_map as $name) { foreach ($callbackMap as $name) {
if (\method_exists($class, $name)) { if (method_exists($class, $name)) {
$worker->$name = [$class, $name]; $worker->$name = [$class, $name];
} }
} }
if (\method_exists($class, 'onWorkerStart')) { if (method_exists($class, 'onWorkerStart')) {
\call_user_func([$class, 'onWorkerStart'], $worker); call_user_func([$class, 'onWorkerStart'], $worker);
} }
} }
/** /**
* Start worker * Start worker
* @param $process_name * @param $processName
* @param $config * @param $config
* @return void * @return void
*/ */
function worker_start($process_name, $config) function worker_start($processName, $config)
{ {
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []); $worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
$property_map = [ $propertyMap = [
'count', 'count',
'user', 'user',
'group', 'group',
@ -444,53 +451,38 @@ function worker_start($process_name, $config)
'transport', 'transport',
'protocol', 'protocol',
]; ];
$worker->name = $process_name; $worker->name = $processName;
foreach ($property_map as $property) { foreach ($propertyMap as $property) {
if (isset($config[$property])) { if (isset($config[$property])) {
$worker->$property = $config[$property]; $worker->$property = $config[$property];
} }
} }
$worker->onWorkerStart = function ($worker) use ($config) { $worker->onWorkerStart = function ($worker) use ($config) {
require_once \base_path() . '/support/bootstrap.php'; require_once base_path('/support/bootstrap.php');
foreach ($config['services'] ?? [] as $server) {
if (!\class_exists($server['handler'])) {
echo "process error: class {$server['handler']} not exists\r\n";
continue;
}
$listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
if (isset($server['listen'])) {
echo "listen: {$server['listen']}\n";
}
$instance = Container::make($server['handler'], $server['constructor'] ?? []);
\worker_bind($listen, $instance);
$listen->listen();
}
if (isset($config['handler'])) { if (isset($config['handler'])) {
if (!\class_exists($config['handler'])) { if (!class_exists($config['handler'])) {
echo "process error: class {$config['handler']} not exists\r\n"; echo "process error: class {$config['handler']} not exists\r\n";
return; return;
} }
$instance = Container::make($config['handler'], $config['constructor'] ?? []); $instance = Container::make($config['handler'], $config['constructor'] ?? []);
\worker_bind($worker, $instance); worker_bind($worker, $instance);
} }
}; };
} }
/** /**
* Get realpath * Get realpath
* @param string $file_path * @param string $filePath
* @return string * @return string
*/ */
function get_realpath(string $file_path): string function get_realpath(string $filePath): string
{ {
if (\strpos($file_path, 'phar://') === 0) { if (strpos($filePath, 'phar://') === 0) {
return $file_path; return $filePath;
} else { } else {
return \realpath($file_path); return realpath($filePath);
} }
} }
@ -500,7 +492,7 @@ function get_realpath(string $file_path): string
*/ */
function is_phar(): bool function is_phar(): bool
{ {
return \class_exists(\Phar::class, false) && Phar::running(); return class_exists(Phar::class, false) && Phar::running();
} }
/** /**
@ -510,16 +502,27 @@ function is_phar(): bool
function cpu_count(): int function cpu_count(): int
{ {
// Windows does not support the number of processes setting. // Windows does not support the number of processes setting.
if (\DIRECTORY_SEPARATOR === '\\') { if (DIRECTORY_SEPARATOR === '\\') {
return 1; return 1;
} }
$count = 4; $count = 4;
if (\is_callable('shell_exec')) { if (is_callable('shell_exec')) {
if (\strtolower(PHP_OS) === 'darwin') { if (strtolower(PHP_OS) === 'darwin') {
$count = (int)\shell_exec('sysctl -n machdep.cpu.core_count'); $count = (int)shell_exec('sysctl -n machdep.cpu.core_count');
} else { } else {
$count = (int)\shell_exec('nproc'); $count = (int)shell_exec('nproc');
} }
} }
return $count > 0 ? $count : 4; return $count > 0 ? $count : 4;
} }
/**
* Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported
* @param string|null $param param's name
* @param mixed|null $default default value
* @return mixed|null
*/
function input(string $param = null, $default = null)
{
return is_null($param) ? request()->all() : request()->input($param, $default);
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Carbon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,14 @@
# carbonphp/carbon-doctrine-types
Types to use Carbon in Doctrine
## Documentation
[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/)
This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
from `nestbot/carbon` package.
Externalization allows to better deal with different versions of dbal. With
version 4.0 of dbal, it no longer sustainable to be compatible with all version
using a single code.

View File

@ -0,0 +1,36 @@
{
"name": "carbonphp/carbon-doctrine-types",
"description": "Types to use Carbon in Doctrine",
"type": "library",
"keywords": [
"date",
"time",
"DateTime",
"Carbon",
"Doctrine"
],
"require": {
"php": "^7.4 || ^8.0"
},
"require-dev": {
"doctrine/dbal": "^3.7.0",
"nesbot/carbon": "^2.71.0 || ^3.0.0",
"phpunit/phpunit": "^10.3"
},
"conflict": {
"doctrine/dbal": "<3.7.0 || >=4.0.0"
},
"license": "MIT",
"autoload": {
"psr-4": {
"Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
}
},
"authors": [
{
"name": "KyleKatarn",
"email": "kylekatarnls@gmail.com"
}
],
"minimum-stability": "dev"
}

View File

@ -1,14 +1,5 @@
<?php <?php
/**
* This file is part of the Carbon package.
*
* (c) Brian Nesbitt <brian@nesbot.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;

View File

@ -0,0 +1,7 @@
<?php
namespace Carbon\Doctrine;
class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Carbon\Doctrine;
class CarbonType extends DateTimeType implements CarbonDoctrineType
{
}

View File

@ -1,20 +1,15 @@
<?php <?php
/**
* This file is part of the Carbon package.
*
* (c) Brian Nesbitt <brian@nesbot.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\CarbonInterface; use Carbon\CarbonInterface;
use DateTimeInterface; use DateTimeInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\ConversionException;
use Exception; use Exception;
@ -23,6 +18,14 @@ use Exception;
*/ */
trait CarbonTypeConverter trait CarbonTypeConverter
{ {
/**
* This property differentiates types installed by carbonphp/carbon-doctrine-types
* from the ones embedded previously in nesbot/carbon source directly.
*
* @readonly
*/
public bool $external = true;
/** /**
* @return class-string<T> * @return class-string<T>
*/ */
@ -31,20 +34,12 @@ trait CarbonTypeConverter
return Carbon::class; return Carbon::class;
} }
/** public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
* @return string
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{ {
$precision = $fieldDeclaration['precision'] ?: 10; $precision = min(
$fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
if ($fieldDeclaration['secondPrecision'] ?? false) { $this->getMaximumPrecision($platform),
$precision = 0; );
}
if ($precision === 10) {
$precision = DateTimeDefaultPrecision::get();
}
$type = parent::getSQLDeclaration($fieldDeclaration, $platform); $type = parent::getSQLDeclaration($fieldDeclaration, $platform);
@ -90,7 +85,7 @@ trait CarbonTypeConverter
if (!$date) { if (!$date) {
throw ConversionException::conversionFailedFormat( throw ConversionException::conversionFailedFormat(
$value, $value,
$this->getName(), $this->getTypeName(),
'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()', 'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
$error $error
); );
@ -101,10 +96,8 @@ trait CarbonTypeConverter
/** /**
* @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return string|null
*/ */
public function convertToDatabaseValue($value, AbstractPlatform $platform) public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{ {
if ($value === null) { if ($value === null) {
return $value; return $value;
@ -116,8 +109,33 @@ trait CarbonTypeConverter
throw ConversionException::conversionFailedInvalidType( throw ConversionException::conversionFailedInvalidType(
$value, $value,
$this->getName(), $this->getTypeName(),
['null', 'DateTime', 'Carbon'] ['null', 'DateTime', 'Carbon']
); );
} }
private function getTypeName(): string
{
$chunks = explode('\\', static::class);
$type = preg_replace('/Type$/', '', end($chunks));
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $type));
}
private function getMaximumPrecision(AbstractPlatform $platform): int
{
if ($platform instanceof DB2Platform) {
return 12;
}
if ($platform instanceof OraclePlatform) {
return 9;
}
if ($platform instanceof SQLServerPlatform || $platform instanceof SqlitePlatform) {
return 3;
}
return 6;
}
} }

View File

@ -1,14 +1,5 @@
<?php <?php
/**
* This file is part of the Carbon package.
*
* (c) Brian Nesbitt <brian@nesbot.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
class DateTimeDefaultPrecision class DateTimeDefaultPrecision

View File

@ -1,9 +1,5 @@
<?php <?php
/**
* Thanks to https://github.com/flaushi for his suggestion:
* https://github.com/doctrine/dbal/issues/2873#issuecomment-534956358
*/
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;

View File

@ -1,9 +1,5 @@
<?php <?php
/**
* Thanks to https://github.com/flaushi for his suggestion:
* https://github.com/doctrine/dbal/issues/2873#issuecomment-534956358
*/
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
use Carbon\Carbon; use Carbon\Carbon;

View File

@ -15,7 +15,6 @@ return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php', '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',

View File

@ -41,6 +41,7 @@ return array(
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'), 'MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'),
@ -62,6 +63,7 @@ return array(
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'), 'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
'DebugBar\\' => array($vendorDir . '/maximebf/debugbar/src/DebugBar'), 'DebugBar\\' => array($vendorDir . '/maximebf/debugbar/src/DebugBar'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), 'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
'App\\' => array($baseDir . '/app'), 'App\\' => array($baseDir . '/app'),
'' => array($baseDir . '/'), '' => array($baseDir . '/'),

View File

@ -16,7 +16,6 @@ class ComposerStaticInitfdb689ed918f2ee4ecdf1e51d93bd946
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php', '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
@ -86,6 +85,7 @@ class ComposerStaticInitfdb689ed918f2ee4ecdf1e51d93bd946
'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16, 'Psr\\Http\\Client\\' => 16,
'Psr\\Container\\' => 14, 'Psr\\Container\\' => 14,
'Psr\\Clock\\' => 10,
'Psr\\Cache\\' => 10, 'Psr\\Cache\\' => 10,
), ),
'M' => 'M' =>
@ -128,6 +128,7 @@ class ComposerStaticInitfdb689ed918f2ee4ecdf1e51d93bd946
'C' => 'C' =>
array ( array (
'Composer\\CaBundle\\' => 18, 'Composer\\CaBundle\\' => 18,
'Carbon\\Doctrine\\' => 16,
'Carbon\\' => 7, 'Carbon\\' => 7,
), ),
'A' => 'A' =>
@ -278,6 +279,10 @@ class ComposerStaticInitfdb689ed918f2ee4ecdf1e51d93bd946
array ( array (
0 => __DIR__ . '/..' . '/psr/container/src', 0 => __DIR__ . '/..' . '/psr/container/src',
), ),
'Psr\\Clock\\' =>
array (
0 => __DIR__ . '/..' . '/psr/clock/src',
),
'Psr\\Cache\\' => 'Psr\\Cache\\' =>
array ( array (
0 => __DIR__ . '/..' . '/psr/cache/src', 0 => __DIR__ . '/..' . '/psr/cache/src',
@ -366,6 +371,10 @@ class ComposerStaticInitfdb689ed918f2ee4ecdf1e51d93bd946
array ( array (
0 => __DIR__ . '/..' . '/composer/ca-bundle/src', 0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
), ),
'Carbon\\Doctrine\\' =>
array (
0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine',
),
'Carbon\\' => 'Carbon\\' =>
array ( array (
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon', 0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',

View File

@ -30,7 +30,7 @@
"symfony/phpunit-bridge": "^4.2 || ^5", "symfony/phpunit-bridge": "^4.2 || ^5",
"phpstan/phpstan": "^0.12.55", "phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0", "psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -1,7 +1,7 @@
## ##
## Bundle of CA Root Certificates ## Bundle of CA Root Certificates
## ##
## Certificate data from Mozilla as of: Tue Jul 19 03:12:06 2022 GMT ## Certificate data from Mozilla as of: Tue Dec 12 04:12:04 2023 GMT
## ##
## This is a bundle of X.509 certificates of public Certificate Authorities ## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates ## (CA). These were automatically extracted from Mozilla's root certificates
@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile. ## Just configure this file as the SSLCACertificateFile.
## ##
## Conversion done with mk-ca-bundle.pl version 1.29. ## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: 9bf3799611fb58197f61d45e71ce3dc19f30e7dd73731915872ce5108a7bb066 ## SHA256: 1970dd65858925d68498d2356aea6d03f764422523c5887deca8ce3ba9e1f845
## ##
@ -200,27 +200,6 @@ vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE----- -----END CERTIFICATE-----
Security Communication Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
FL39vmwLAw==
-----END CERTIFICATE-----
XRamp Global CA Root XRamp Global CA Root
==================== ====================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -489,29 +468,6 @@ IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
-----END CERTIFICATE----- -----END CERTIFICATE-----
Network Solutions Certificate Authority
=======================================
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
COMODO ECC Certification Authority COMODO ECC Certification Authority
================================== ==================================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -626,26 +582,6 @@ NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE----- -----END CERTIFICATE-----
Hongkong Post Root CA 1
=======================
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
-----END CERTIFICATE-----
SecureSign RootCA11 SecureSign RootCA11
=================== ===================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -712,39 +648,6 @@ YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
kpeDMdmztcpHWD9f kpeDMdmztcpHWD9f
-----END CERTIFICATE----- -----END CERTIFICATE-----
Autoridad de Certificacion Firmaprofesional CIF A62634068
=========================================================
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----
Izenpe.com Izenpe.com
========== ==========
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -1284,40 +1187,6 @@ Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE----- -----END CERTIFICATE-----
E-Tugra Certification Authority
===============================
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
C7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----
T-TeleSec GlobalRoot Class 2 T-TeleSec GlobalRoot Class 2
============================ ============================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -1654,36 +1523,6 @@ uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE----- -----END CERTIFICATE-----
Staat der Nederlanden EV Root CA
================================
-----BEGIN CERTIFICATE-----
MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
-----END CERTIFICATE-----
IdenTrust Commercial Root CA 1 IdenTrust Commercial Root CA 1
============================== ==============================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -2135,87 +1974,6 @@ F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ
aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
-----END CERTIFICATE----- -----END CERTIFICATE-----
TrustCor RootCert CA-1
======================
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP
MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx
MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu
YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe
VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy
dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq
jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4
pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0
JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h
gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw
/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j
BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5
mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C
qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P
3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk=
-----END CERTIFICATE-----
TrustCor RootCert CA-2
======================
-----BEGIN CERTIFICATE-----
MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w
DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT
eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0
eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy
MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h
bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0
IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb
ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk
RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1
oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb
XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1
/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q
jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP
eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg
rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU
2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h
Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp
kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv
2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3
S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw
PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv
DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU
RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE
xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX
RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ
-----END CERTIFICATE-----
TrustCor ECA-1
==============
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP
MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw
N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5
MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y
IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR
MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23
xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc
p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+
fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj
YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL
f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u
/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs
J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC
jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g==
-----END CERTIFICATE-----
SSL.com Root Certification Authority RSA SSL.com Root Certification Authority RSA
======================================== ========================================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -3410,51 +3168,367 @@ AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8
rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE----- -----END CERTIFICATE-----
E-Tugra Global Root CA RSA v3 Security Communication RootCA3
============================= ==============================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAxCzAJ MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQMSUw
BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAb IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1cml0eSBD
BgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290 b21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQsw
IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJU CQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UE
UjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRF AxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
LVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0Eg MIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4rCmDvu20r
djMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J77gnJY9LTQ91ew6aEOErx hvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzAlrenfna84xtSGc4RHwsE
jYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscxuj7X/iWpKo429NEvx7epXTPcMHD4QGxL NPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MGTfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2
sqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF /D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGm
/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8q npjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtY
QedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrw XLVqAvO4g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPK
bMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg6 p7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC
04nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLB 3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOf
eSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiM GAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0Vcw
bIedBi3x7+PmBvrFZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbg CBEF/VfR2ccCAwEAAaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB
h3cXTJ2w2AmoDVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD /wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS
AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1 YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHu
LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ Tofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O
gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN4 H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASx
38o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/q YfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZ
ln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3s XSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml
SdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn99t2HVhjY +LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUVnuiZIesn
sCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQmhty3QUBjYZgv6Rn7rWl KwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD2NCcnWXL0CsnMQMeNuE9
DdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbHEqIbZULpkejLPoeJVF3Zr52X dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm
nGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFH 6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR16/9M51NZg==
IK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiX
YY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ
-----END CERTIFICATE----- -----END CERTIFICATE-----
E-Tugra Global Root CA ECC v3 Security Communication ECC RootCA1
============================= ==================================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD
BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t
BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL
IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV
MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA
Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo
djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2 5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW
w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31 BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK
Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L
zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e
PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W N9k=
Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3 -----END CERTIFICATE-----
BJCA Global Root CA1
====================
-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQG
EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJK
Q0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAzMTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkG
A1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQD
DBRCSkNBIEdsb2JhbCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFm
CL3ZxRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZspDyRhyS
sTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O558dnJCNPYwpj9mZ9S1Wn
P3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgRat7GGPZHOiJBhyL8xIkoVNiMpTAK+BcW
yqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRj
eulumijWML3mG90Vr4TqnMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNn
MoH1V6XKV0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/pj+b
OT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZOz2nxbkRs1CTqjSSh
GL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXnjSXWgXSHRtQpdaJCbPdzied9v3pK
H9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMB
AAGjQjBAMB0GA1UdDgQWBBTF7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4
YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3KliawLwQ8hOnThJ
dMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u+2D2/VnGKhs/I0qUJDAnyIm8
60Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuh
TaRjAv04l5U/BXCga99igUOLtFkNSoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW
4AB+dAb/OMRyHdOoP2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmp
GQrI+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRzznfSxqxx
4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9eVzYH6Eze9mCUAyTF6ps
3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4S
SPfSKcOYKMryMguTjClPPGAyzQWWYezyr/6zcCwupvI=
-----END CERTIFICATE-----
BJCA Global Root CA2
====================
-----BEGIN CERTIFICATE-----
MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQGEwJD
TjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJKQ0Eg
R2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UE
BhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRC
SkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jl
SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK
/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJKsVF/BvDRgh9Obl+rg/xI
1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8
W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g
UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
-----END CERTIFICATE-----
Sectigo Public Server Authentication Root E46
=============================================
-----BEGIN CERTIFICATE-----
MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQswCQYDVQQGEwJH
QjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBTZXJ2
ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5
WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0
aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccCWvkEN/U0
NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+6xnOQ6OjQjBAMB0GA1Ud
DgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAKBggqhkjOPQQDAwNnADBkAjAn7qRaqCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RH
lAFWovgzJQxC36oCMB3q4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21U
SAGKcw==
-----END CERTIFICATE-----
Sectigo Public Server Authentication Root R46
=============================================
-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBfMQswCQYDVQQG
EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1
OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3
DQEBAQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDaef0rty2k
1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnzSDBh+oF8HqcIStw+Kxwf
GExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xfiOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMP
FF1bFOdLvt30yNoDN9HWOaEhUTCDsG3XME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vu
ZDCQOc2TZYEhMbUjUDM3IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5Qaz
Yw6A3OASVYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgESJ/A
wSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu+Zd4KKTIRJLpfSYF
plhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt8uaZFURww3y8nDnAtOFr94MlI1fZ
EoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+LHaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW
6aWWrL3DkJiy4Pmi1KZHQ3xtzwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWI
IUkwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQYKlJfp/imTYp
E0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52gDY9hAaLMyZlbcp+nv4fjFg4
exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZAFv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M
0ejf5lG5Nkc/kLnHvALcWxxPDkjBJYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI
84HxZmduTILA7rpXDhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9m
pFuiTdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5dHn5Hrwd
Vw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65LvKRRFHQV80MNNVIIb/b
E/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmm
J1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAYQqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
-----END CERTIFICATE-----
SSL.com TLS RSA Root CA 2022
============================
-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQG
EwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBSU0Eg
Um9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloXDTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMC
VVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJv
b3QgQ0EgMjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u
9nTPL3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OYt6/wNr/y
7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0insS657Lb85/bRi3pZ7Qcac
oOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3PnxEX4MN8/HdIGkWCVDi1FW24IBydm5M
R7d1VVm0U3TZlMZBrViKMWYPHqIbKUBOL9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDG
D6C1vBdOSHtRwvzpXGk3R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEW
TO6Af77wdr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS+YCk
8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYSd66UNHsef8JmAOSq
g+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoGAtUjHBPW6dvbxrB6y3snm/vg1UYk
7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2fgTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1Ud
EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsu
N+7jhHonLs0ZNbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt
hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsMQtfhWsSWTVTN
j8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvfR4iyrT7gJ4eLSYwfqUdYe5by
iB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJDPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjU
o3KUQyxi4U5cMj29TH0ZR6LDSeeWP4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqo
ENjwuSfr98t67wVylrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7Egkaib
MOlqbLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2wAgDHbICi
vRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3qr5nsLFR+jM4uElZI7xc7
P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sjiMho6/4UIyYOf8kpIEFR3N+2ivEC+5BB0
9+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
-----END CERTIFICATE-----
SSL.com TLS ECC Root CA 2022
============================
-----BEGIN CERTIFICATE-----
MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV
UzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBFQ0MgUm9v
dCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMx
GDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3Qg
Q0EgMjAyMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWy
JGYmacCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFNSeR7T5v1
5wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSJjy+j6CugFFR7
81a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NWuCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGG
MAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w
7deedWo1dlJF4AIxAMeNb0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5
Zn6g6g==
-----END CERTIFICATE-----
Atos TrustedRoot Root CA ECC TLS 2021
=====================================
-----BEGIN CERTIFICATE-----
MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4wLAYDVQQDDCVB
dG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQswCQYD
VQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3Mg
VHJ1c3RlZFJvb3QgUm9vdCBDQSBFQ0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYT
AkRFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6K
DP/XtXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4AjJn8ZQS
b+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2KCXWfeBmmnoJsmo7jjPX
NtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwW5kp85wxtolrbNa9d+F851F+
uDrNozZffPc8dz7kUK2o59JZDCaOMDtuCCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGY
a3cpetskz2VAv9LcjBHo9H1/IISpQuQo
-----END CERTIFICATE-----
Atos TrustedRoot Root CA RSA TLS 2021
=====================================
-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBMMS4wLAYDVQQD
DCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQsw
CQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0
b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNV
BAYTAkRFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BB
l01Z4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYvYe+W/CBG
vevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZkmGbzSoXfduP9LVq6hdK
ZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDsGY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt
0xU6kGpn8bRrZtkh68rZYnxGEFzedUlnnkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVK
PNe0OwANwI8f4UDErmwh3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMY
sluMWuPD0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzygeBY
Br3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8ANSbhqRAvNncTFd+
rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezBc6eUWsuSZIKmAMFwoW4sKeFYV+xa
fJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lIpw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUdEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0G
CSqGSIb3DQEBDAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS
4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPso0UvFJ/1TCpl
Q3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJqM7F78PRreBrAwA0JrRUITWX
AdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuywxfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9G
slA9hGCZcbUztVdF5kJHdWoOsAgMrr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2Vkt
afcxBPTy+av5EzH4AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9q
TFsR0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuYo7Ey7Nmj
1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5dDTedk+SKlOxJTnbPP/l
PqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcEoji2jbDwN/zIIX8/syQbPYtuzE2wFg2W
HYMfRsCbvUOZ58SWLs5fyQ==
-----END CERTIFICATE-----
TrustAsia Global Root CA G3
===========================
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEMBQAwWjELMAkG
A1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMM
G1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAeFw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEw
MTlaMFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMu
MSQwIgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNST1QY4Sxz
lZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqKAtCWHwDNBSHvBm3dIZwZ
Q0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/V
P68czH5GX6zfZBCK70bwkPAPLfSIC7Epqq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1Ag
dB4SQXMeJNnKziyhWTXAyB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm
9WAPzJMshH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gXzhqc
D0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAvkV34PmVACxmZySYg
WmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msTf9FkPz2ccEblooV7WIQn3MSAPmea
mseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jAuPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCF
TIcQcf+eQxuulXUtgQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj
7zjKsK5Xf/IhMBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E
BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4wM8zAQLpw6o1
D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2XFNFV1pF1AWZLy4jVe5jaN/T
G3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNj
duMNhXJEIlU/HHzp/LgV6FL6qj6jITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstl
cHboCoWASzY9M/eVVHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys
+TIxxHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1onAX1daBli
2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d7XB4tmBZrOFdRWOPyN9y
aFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2NtjjgKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsAS
ZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFR
JQJ6+N1rZdVtTTDIZbpoFGWsJwt0ivKH
-----END CERTIFICATE-----
TrustAsia Global Root CA G4
===========================
-----BEGIN CERTIFICATE-----
MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMwWjELMAkGA1UE
BhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMMG1Ry
dXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0yMTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJa
MFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQw
IgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
AATxs8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbwLxYI+hW8
m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJijYzBhMA8GA1UdEwEB/wQF
MAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mDpm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/
pDHel4NZg6ZvccveMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AA
bbd+NvBNEU/zy4k6LHiRUKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xk
dUfFVZDj/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA==
-----END CERTIFICATE-----
CommScope Public Trust ECC Root-01
==================================
-----BEGIN CERTIFICATE-----
MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMwTjELMAkGA1UE
BhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBUcnVz
dCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNaFw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYT
AlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3Qg
RUNDIFJvb3QtMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLx
eP0CflfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJEhRGnSjot
6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggqhkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2
Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liW
pDVfG2XqYZpwI7UNo5uSUm9poIyNStDuiw7LR47QjRE=
-----END CERTIFICATE-----
CommScope Public Trust ECC Root-02
==================================
-----BEGIN CERTIFICATE-----
MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMwTjELMAkGA1UE
BhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBUcnVz
dCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRaFw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYT
AlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3Qg
RUNDIFJvb3QtMDIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/M
MDALj2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmUv4RDsNuE
SgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggqhkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9
Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/nich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs7
3u1Z/GtMMH9ZzkXpc2AVmkzw5l4lIhVtwodZ0LKOag==
-----END CERTIFICATE-----
CommScope Public Trust RSA Root-01
==================================
-----BEGIN CERTIFICATE-----
MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQELBQAwTjELMAkG
A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBU
cnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNV
BAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1
c3QgUlNBIFJvb3QtMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45Ft
nYSkYZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslhsuitQDy6
uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0alDrJLpA6lfO741GIDuZNq
ihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3OjWiE260f6GBfZumbCk6SP/F2krfxQapWs
vCQz0b2If4b19bJzKo98rwjyGpg/qYFlP8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/c
Zip8UlF1y5mO6D1cv547KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTif
BSeolz7pUcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/kQO9
lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JOHg9O5j9ZpSPcPYeo
KFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkBEa801M/XrmLTBQe0MXXgDW1XT2mH
+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6UCBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm4
5P3luG0wDQYJKoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6
NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQnmhUQo8mUuJM
3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+QgvfKNmwrZggvkN80V4aCRck
jXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2vtrV0KnahP/t1MJ+UXjulYPPLXAziDslg+Mkf
Foom3ecnf+slpoq9uC02EJqxWE2aaE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/W
NyVntHKLr4W96ioDj8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+
o/E4Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0wlREQKC6/
oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHnYfkUyq+Dj7+vsQpZXdxc
1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVocicCMb3SgazNNtQEo/a2tiRc7ppqEvOuM
6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw
-----END CERTIFICATE-----
CommScope Public Trust RSA Root-02
==================================
-----BEGIN CERTIFICATE-----
MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQELBQAwTjELMAkG
A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBU
cnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNV
BAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1
c3QgUlNBIFJvb3QtMDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3V
rCLENQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0kyI9p+Kx
7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1CrWDaSWqVcN3SAOLMV2MC
e5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxzhkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2W
Wy09X6GDRl224yW4fKcZgBzqZUPckXk2LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rp
M9kzXzehxfCrPfp4sOcsn/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIf
hs1w/tkuFT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5kQMr
eyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3wNemKfrb3vOTlycE
VS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6vwQcQeKwRoi9C8DfF8rhW3Q5iLc4t
Vn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7Gx
cJXvYXowDQYJKoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB
KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3+VGXu6TwYofF
1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbymeAPnCKfWxkxlSaRosTKCL4BWa
MS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3NyqpgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xd
gSGn2rtO/+YHqP65DSdsu3BaVXoT6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2O
HG1QAk8mGEPej1WFsQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+Nm
YWvtPjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2dlklyALKr
dVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670v64fG9PiO/yzcnMcmyiQ
iRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17Org3bhzjlP1v9mxnhMUF6cKojawHhRUzN
lM47ni3niAIi9G7oyOzWPPO5std3eqx7
-----END CERTIFICATE----- -----END CERTIFICATE-----

File diff suppressed because it is too large Load Diff

View File

@ -3,26 +3,35 @@
'name' => 'laysense/highspeaker', 'name' => 'laysense/highspeaker',
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => '2a00928da5ff5423aef1d1833239266003781e49', 'reference' => 'b5ff5e8b5f9960d37346ed85377268d733ae16e1',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'dev' => true, 'dev' => true,
), ),
'versions' => array( 'versions' => array(
'carbonphp/carbon-doctrine-types' => array(
'pretty_version' => '2.1.0',
'version' => '2.1.0.0',
'reference' => '99f76ffa36cce3b70a4a6abce41dba15ca2e84cb',
'type' => 'library',
'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
'aliases' => array(),
'dev_requirement' => false,
),
'composer/ca-bundle' => array( 'composer/ca-bundle' => array(
'pretty_version' => '1.3.3', 'pretty_version' => '1.4.0',
'version' => '1.3.3.0', 'version' => '1.4.0.0',
'reference' => '30897edbfb15e784fe55587b4f73ceefd3c4d98c', 'reference' => 'b66d11b7479109ab547f9405b97205640b17d385',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/./ca-bundle', 'install_path' => __DIR__ . '/./ca-bundle',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'doctrine/inflector' => array( 'doctrine/inflector' => array(
'pretty_version' => '2.0.6', 'pretty_version' => '2.0.9',
'version' => '2.0.6.0', 'version' => '2.0.9.0',
'reference' => 'd9d313a36c872fd6ee06d9a6cbcf713eaa40f024', 'reference' => '2930cd5ef353871c821d5c43ed030d39ac8cfe65',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/inflector', 'install_path' => __DIR__ . '/../doctrine/inflector',
'aliases' => array(), 'aliases' => array(),
@ -38,80 +47,80 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/guzzle' => array( 'guzzlehttp/guzzle' => array(
'pretty_version' => '7.5.0', 'pretty_version' => '7.8.1',
'version' => '7.5.0.0', 'version' => '7.8.1.0',
'reference' => 'b50a2a1251152e43f6a37f0fa053e730a67d25ba', 'reference' => '41042bc7ab002487b876a0683fc8dce04ddce104',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/promises' => array( 'guzzlehttp/promises' => array(
'pretty_version' => '1.5.2', 'pretty_version' => '2.0.2',
'version' => '1.5.2.0', 'version' => '2.0.2.0',
'reference' => 'b94b2807d85443f9719887892882d0329d1e2598', 'reference' => 'bbff78d96034045e58e13dedd6ad91b5d1253223',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/promises', 'install_path' => __DIR__ . '/../guzzlehttp/promises',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/psr7' => array( 'guzzlehttp/psr7' => array(
'pretty_version' => '2.4.3', 'pretty_version' => '2.6.2',
'version' => '2.4.3.0', 'version' => '2.6.2.0',
'reference' => '67c26b443f348a51926030c83481b85718457d3d', 'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/bus' => array( 'illuminate/bus' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => 'c7f09872054f2b361f8ed9e9e988b3c9be06c596', 'reference' => '4c719a19c3d8c34b2494a7206f8ffde3eff3f983',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/bus', 'install_path' => __DIR__ . '/../illuminate/bus',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/collections' => array( 'illuminate/collections' => array(
'pretty_version' => 'v9.45.0', 'pretty_version' => 'v9.52.16',
'version' => '9.45.0.0', 'version' => '9.52.16.0',
'reference' => '7a8afa0875d7de162f30865d9fae33c8fb235fa2', 'reference' => 'd3710b0b244bfc62c288c1a87eaa62dd28352d1f',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/collections', 'install_path' => __DIR__ . '/../illuminate/collections',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/conditionable' => array( 'illuminate/conditionable' => array(
'pretty_version' => 'v9.45.0', 'pretty_version' => 'v9.52.16',
'version' => '9.45.0.0', 'version' => '9.52.16.0',
'reference' => '5b40f51ccb07e0e7b1ec5559d8db9e0e2dc51883', 'reference' => 'bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/conditionable', 'install_path' => __DIR__ . '/../illuminate/conditionable',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/container' => array( 'illuminate/container' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => '8ca3036459e26dc7cdedaf0f882b625757cc341e', 'reference' => '1641dda2d0750b68bb1264a3b37ff3973f2e6265',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/container', 'install_path' => __DIR__ . '/../illuminate/container',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/contracts' => array( 'illuminate/contracts' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => 'c7cc6e6198cac6dfdead111f9758de25413188b7', 'reference' => '44f65d723b13823baa02ff69751a5948bde60c22',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/contracts', 'install_path' => __DIR__ . '/../illuminate/contracts',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/events' => array( 'illuminate/events' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => '8e534676bac23bc17925f5c74c128f9c09b98f69', 'reference' => '8e534676bac23bc17925f5c74c128f9c09b98f69',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/events', 'install_path' => __DIR__ . '/../illuminate/events',
@ -119,8 +128,8 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/macroable' => array( 'illuminate/macroable' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => 'e3bfaf6401742a9c6abca61b9b10e998e5b6449a', 'reference' => 'e3bfaf6401742a9c6abca61b9b10e998e5b6449a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/macroable', 'install_path' => __DIR__ . '/../illuminate/macroable',
@ -128,8 +137,8 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/pipeline' => array( 'illuminate/pipeline' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => 'e0be3f3f79f8235ad7334919ca4094d5074e02f6', 'reference' => 'e0be3f3f79f8235ad7334919ca4094d5074e02f6',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/pipeline', 'install_path' => __DIR__ . '/../illuminate/pipeline',
@ -137,27 +146,27 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/redis' => array( 'illuminate/redis' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => '0684a40c7f820b274acdc57e19dc691609de9097', 'reference' => 'd4702b8d6a64a48cfab7259248ecbecf3b6135e2',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/redis', 'install_path' => __DIR__ . '/../illuminate/redis',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/support' => array( 'illuminate/support' => array(
'pretty_version' => 'v9.45.1', 'pretty_version' => 'v9.52.16',
'version' => '9.45.1.0', 'version' => '9.52.16.0',
'reference' => 'd7f7c07e35a2c09cbeeddc0168826cf05a2eeb84', 'reference' => '223c608dbca27232df6213f776bfe7bdeec24874',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/support', 'install_path' => __DIR__ . '/../illuminate/support',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'laysense/dns' => array( 'laysense/dns' => array(
'pretty_version' => '0.0.4', 'pretty_version' => '0.1.0',
'version' => '0.0.4.0', 'version' => '0.1.0.0',
'reference' => 'e6c19f90a14d4b7ef69305b6441889bea545bbce', 'reference' => '8fc2a4df78120a65bbcafc5bc64b83f789822e43',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../laysense/dns', 'install_path' => __DIR__ . '/../laysense/dns',
'aliases' => array(), 'aliases' => array(),
@ -166,25 +175,25 @@
'laysense/highspeaker' => array( 'laysense/highspeaker' => array(
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => '2a00928da5ff5423aef1d1833239266003781e49', 'reference' => 'b5ff5e8b5f9960d37346ed85377268d733ae16e1',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'maximebf/debugbar' => array( 'maximebf/debugbar' => array(
'pretty_version' => 'v1.18.1', 'pretty_version' => 'v1.19.1',
'version' => '1.18.1.0', 'version' => '1.19.1.0',
'reference' => 'ba0af68dd4316834701ecb30a00ce9604ced3ee9', 'reference' => '03dd40a1826f4d585ef93ef83afa2a9874a00523',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../maximebf/debugbar', 'install_path' => __DIR__ . '/../maximebf/debugbar',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'maxmind-db/reader' => array( 'maxmind-db/reader' => array(
'pretty_version' => 'v1.11.0', 'pretty_version' => 'v1.11.1',
'version' => '1.11.0.0', 'version' => '1.11.1.0',
'reference' => 'b1f3c0699525336d09cc5161a2861268d9f2ae5b', 'reference' => '1e66f73ffcf25e17c7a910a1317e9720a95497c7',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../maxmind-db/reader', 'install_path' => __DIR__ . '/../maxmind-db/reader',
'aliases' => array(), 'aliases' => array(),
@ -200,18 +209,18 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'monolog/monolog' => array( 'monolog/monolog' => array(
'pretty_version' => '2.8.0', 'pretty_version' => '2.9.2',
'version' => '2.8.0.0', 'version' => '2.9.2.0',
'reference' => '720488632c590286b88b80e62aa3d3d551ad4a50', 'reference' => '437cb3628f4cf6042cc10ae97fc2b8472e48ca1f',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../monolog/monolog', 'install_path' => __DIR__ . '/../monolog/monolog',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'nesbot/carbon' => array( 'nesbot/carbon' => array(
'pretty_version' => '2.64.0', 'pretty_version' => '2.72.2',
'version' => '2.64.0.0', 'version' => '2.72.2.0',
'reference' => '889546413c97de2d05063b8cb7b193c2531ea211', 'reference' => '3e7edc41b58d65509baeb0d4a14c8fa41d627130',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../nesbot/carbon', 'install_path' => __DIR__ . '/../nesbot/carbon',
'aliases' => array(), 'aliases' => array(),
@ -241,6 +250,21 @@
0 => '2.0|3.0', 0 => '2.0|3.0',
), ),
), ),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/clock',
'aliases' => array(),
'dev_requirement' => false,
),
'psr/clock-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/container' => array( 'psr/container' => array(
'pretty_version' => '2.0.2', 'pretty_version' => '2.0.2',
'version' => '2.0.2.0', 'version' => '2.0.2.0',
@ -257,9 +281,9 @@
), ),
), ),
'psr/http-client' => array( 'psr/http-client' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '1.0.3',
'version' => '1.0.1.0', 'version' => '1.0.3.0',
'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-client', 'install_path' => __DIR__ . '/../psr/http-client',
'aliases' => array(), 'aliases' => array(),
@ -272,9 +296,9 @@
), ),
), ),
'psr/http-factory' => array( 'psr/http-factory' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '1.0.2',
'version' => '1.0.1.0', 'version' => '1.0.2.0',
'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', 'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory', 'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(), 'aliases' => array(),
@ -287,9 +311,9 @@
), ),
), ),
'psr/http-message' => array( 'psr/http-message' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '2.0',
'version' => '1.0.1.0', 'version' => '2.0.0.0',
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message', 'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(), 'aliases' => array(),
@ -351,9 +375,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/cache' => array( 'symfony/cache' => array(
'pretty_version' => 'v6.0.16', 'pretty_version' => 'v6.0.19',
'version' => '6.0.16.0', 'version' => '6.0.19.0',
'reference' => '40cd2323c219e30292c0e2520572b54310e534c6', 'reference' => '81ca309f056e836480928b20280ec52ce8369bb3',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/cache', 'install_path' => __DIR__ . '/../symfony/cache',
'aliases' => array(), 'aliases' => array(),
@ -375,9 +399,9 @@
), ),
), ),
'symfony/console' => array( 'symfony/console' => array(
'pretty_version' => 'v6.0.16', 'pretty_version' => 'v6.0.19',
'version' => '6.0.16.0', 'version' => '6.0.19.0',
'reference' => 'be294423f337dda97c810733138c0caec1bb0575', 'reference' => 'c3ebc83d031b71c39da318ca8b7a07ecc67507ed',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console', 'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(), 'aliases' => array(),
@ -393,45 +417,45 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-ctype' => array( 'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.27.0', 'pretty_version' => 'v1.28.0',
'version' => '1.27.0.0', 'version' => '1.28.0.0',
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a', 'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-intl-grapheme' => array( 'symfony/polyfill-intl-grapheme' => array(
'pretty_version' => 'v1.27.0', 'pretty_version' => 'v1.28.0',
'version' => '1.27.0.0', 'version' => '1.28.0.0',
'reference' => '511a08c03c1960e08a883f4cffcacd219b758354', 'reference' => '875e90aeea2777b6f135677f618529449334a612',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-intl-normalizer' => array( 'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.27.0', 'pretty_version' => 'v1.28.0',
'version' => '1.27.0.0', 'version' => '1.28.0.0',
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6', 'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-mbstring' => array( 'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.27.0', 'pretty_version' => 'v1.28.0',
'version' => '1.27.0.0', 'version' => '1.28.0.0',
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534', 'reference' => '42292d99c55abe617799667f454222c54c60e229',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php80' => array( 'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.27.0', 'pretty_version' => 'v1.28.0',
'version' => '1.27.0.0', 'version' => '1.28.0.0',
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936', 'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80', 'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(), 'aliases' => array(),
@ -447,18 +471,18 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/string' => array( 'symfony/string' => array(
'pretty_version' => 'v6.0.15', 'pretty_version' => 'v6.0.19',
'version' => '6.0.15.0', 'version' => '6.0.19.0',
'reference' => '51ac0fa0ccf132a00519b87c97e8f775fa14e771', 'reference' => 'd9e72497367c23e08bf94176d2be45b00a9d232a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string', 'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/translation' => array( 'symfony/translation' => array(
'pretty_version' => 'v6.0.14', 'pretty_version' => 'v6.0.19',
'version' => '6.0.14.0', 'version' => '6.0.19.0',
'reference' => '6f99eb179aee4652c0a7cd7c11f2a870d904330c', 'reference' => '9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation', 'install_path' => __DIR__ . '/../symfony/translation',
'aliases' => array(), 'aliases' => array(),
@ -480,18 +504,18 @@
), ),
), ),
'symfony/var-dumper' => array( 'symfony/var-dumper' => array(
'pretty_version' => 'v6.0.14', 'pretty_version' => 'v6.0.19',
'version' => '6.0.14.0', 'version' => '6.0.19.0',
'reference' => '72af925ddd41ca0372d166d004bc38a00c4608cc', 'reference' => 'eb980457fa6899840fe1687e8627a03a7d8a3d52',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-dumper', 'install_path' => __DIR__ . '/../symfony/var-dumper',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'symfony/var-exporter' => array( 'symfony/var-exporter' => array(
'pretty_version' => 'v6.0.10', 'pretty_version' => 'v6.0.19',
'version' => '6.0.10.0', 'version' => '6.0.19.0',
'reference' => 'e3df004a8d0fb572c420a6915cd23db9254c8366', 'reference' => 'df56f53818c2d5d9f683f4ad2e365ba73a3b69d2',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-exporter', 'install_path' => __DIR__ . '/../symfony/var-exporter',
'aliases' => array(), 'aliases' => array(),
@ -507,63 +531,63 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'webman-tech/debugbar' => array( 'webman-tech/debugbar' => array(
'pretty_version' => 'v2.1.0', 'pretty_version' => 'v2.2.1',
'version' => '2.1.0.0', 'version' => '2.2.1.0',
'reference' => '723baf3aa623dba5ad70205f5afa09615524e7e1', 'reference' => 'f448183bb2a2fe3dce8c4de59f5668611e4e2e8b',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../webman-tech/debugbar', 'install_path' => __DIR__ . '/../webman-tech/debugbar',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'webman/console' => array( 'webman/console' => array(
'pretty_version' => 'v1.2.19', 'pretty_version' => 'v1.3.4',
'version' => '1.2.19.0', 'version' => '1.3.4.0',
'reference' => '9db7a3cfccb84cc45561d0ca8b3927e9e4c023ec', 'reference' => 'ee50a1eca292eea5bf70661aa2ef722e1294814c',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../webman/console', 'install_path' => __DIR__ . '/../webman/console',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'workerman/validation' => array( 'workerman/validation' => array(
'pretty_version' => 'v3.0.2', 'pretty_version' => 'v3.1.2',
'version' => '3.0.2.0', 'version' => '3.1.2.0',
'reference' => '49387fff74acb63277ea7ed9a476ffe339348772', 'reference' => 'a4e9896e76b2fac92aff9a9f784df55f615571a0',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../workerman/validation', 'install_path' => __DIR__ . '/../workerman/validation',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'workerman/webman-framework' => array( 'workerman/webman-framework' => array(
'pretty_version' => 'v1.4.10', 'pretty_version' => 'v1.5.16',
'version' => '1.4.10.0', 'version' => '1.5.16.0',
'reference' => 'd9d6a5317f1f11486e37bf5613aa0d1601b83edd', 'reference' => '84335520a340ee60adf7cf17aeb0edb9536c24e8',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../workerman/webman-framework', 'install_path' => __DIR__ . '/../workerman/webman-framework',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'workerman/workerman' => array( 'workerman/workerman' => array(
'pretty_version' => 'v4.1.5', 'pretty_version' => 'v4.1.14',
'version' => '4.1.5.0', 'version' => '4.1.14.0',
'reference' => '16bcfc2c7574feea46cdadaaa8ae73f14d464b21', 'reference' => 'f7c9667c7b5387c01fa9e50ee79ed931e93ee76e',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../workerman/workerman', 'install_path' => __DIR__ . '/../workerman/workerman',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'yzh52521/easyhttp' => array( 'yzh52521/easyhttp' => array(
'pretty_version' => 'v1.0.5', 'pretty_version' => 'v1.1.3',
'version' => '1.0.5.0', 'version' => '1.1.3.0',
'reference' => 'a74fa5a1d4f701bd20e581b0731e885aac3daf9f', 'reference' => '02bcf47eaf723520fa3905d0e6f1852168fe646c',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../yzh52521/easyhttp', 'install_path' => __DIR__ . '/../yzh52521/easyhttp',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'yzh52521/webman-throttle' => array( 'yzh52521/webman-throttle' => array(
'pretty_version' => 'v1.0.10', 'pretty_version' => 'v1.1.0',
'version' => '1.0.10.0', 'version' => '1.1.0.0',
'reference' => '66c69086bd98f3ccb5fc2bf9a59b9f02516384a5', 'reference' => '949304805778a99bb9437179234d73d7c3c6205f',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../yzh52521/webman-throttle', 'install_path' => __DIR__ . '/../yzh52521/webman-throttle',
'aliases' => array(), 'aliases' => array(),

View File

@ -16,12 +16,12 @@
"php": "^7.2 || ^8.0" "php": "^7.2 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^10", "doctrine/coding-standard": "^11.0",
"phpstan/phpstan": "^1.8", "phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.3", "phpstan/phpstan-strict-rules": "^1.3",
"phpunit/phpunit": "^8.5 || ^9.5", "phpunit/phpunit": "^8.5 || ^9.5",
"vimeo/psalm": "^4.25" "vimeo/psalm": "^4.25 || ^5.4"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -93,16 +93,19 @@ class Inflectible
public static function getIrregular(): iterable public static function getIrregular(): iterable
{ {
yield new Substitution(new Word('atlas'), new Word('atlases')); yield new Substitution(new Word('atlas'), new Word('atlases'));
yield new Substitution(new Word('axis'), new Word('axes'));
yield new Substitution(new Word('axe'), new Word('axes')); yield new Substitution(new Word('axe'), new Word('axes'));
yield new Substitution(new Word('beef'), new Word('beefs')); yield new Substitution(new Word('beef'), new Word('beefs'));
yield new Substitution(new Word('blouse'), new Word('blouses')); yield new Substitution(new Word('blouse'), new Word('blouses'));
yield new Substitution(new Word('brother'), new Word('brothers')); yield new Substitution(new Word('brother'), new Word('brothers'));
yield new Substitution(new Word('cafe'), new Word('cafes')); yield new Substitution(new Word('cafe'), new Word('cafes'));
yield new Substitution(new Word('cave'), new Word('caves'));
yield new Substitution(new Word('chateau'), new Word('chateaux')); yield new Substitution(new Word('chateau'), new Word('chateaux'));
yield new Substitution(new Word('niveau'), new Word('niveaux')); yield new Substitution(new Word('niveau'), new Word('niveaux'));
yield new Substitution(new Word('child'), new Word('children')); yield new Substitution(new Word('child'), new Word('children'));
yield new Substitution(new Word('canvas'), new Word('canvases')); yield new Substitution(new Word('canvas'), new Word('canvases'));
yield new Substitution(new Word('cookie'), new Word('cookies')); yield new Substitution(new Word('cookie'), new Word('cookies'));
yield new Substitution(new Word('brownie'), new Word('brownies'));
yield new Substitution(new Word('corpus'), new Word('corpuses')); yield new Substitution(new Word('corpus'), new Word('corpuses'));
yield new Substitution(new Word('cow'), new Word('cows')); yield new Substitution(new Word('cow'), new Word('cows'));
yield new Substitution(new Word('criterion'), new Word('criteria')); yield new Substitution(new Word('criterion'), new Word('criteria'));
@ -110,6 +113,7 @@ class Inflectible
yield new Substitution(new Word('demo'), new Word('demos')); yield new Substitution(new Word('demo'), new Word('demos'));
yield new Substitution(new Word('domino'), new Word('dominoes')); yield new Substitution(new Word('domino'), new Word('dominoes'));
yield new Substitution(new Word('echo'), new Word('echoes')); yield new Substitution(new Word('echo'), new Word('echoes'));
yield new Substitution(new Word('epoch'), new Word('epochs'));
yield new Substitution(new Word('foot'), new Word('feet')); yield new Substitution(new Word('foot'), new Word('feet'));
yield new Substitution(new Word('fungus'), new Word('fungi')); yield new Substitution(new Word('fungus'), new Word('fungi'));
yield new Substitution(new Word('ganglion'), new Word('ganglions')); yield new Substitution(new Word('ganglion'), new Word('ganglions'));

View File

@ -2,6 +2,75 @@
Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version. Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
## 7.8.1 - 2023-12-03
### Changed
- Updated links in docs to their canonical versions
- Replaced `call_user_func*` with native calls
## 7.8.0 - 2023-08-27
### Added
- Support for PHP 8.3
- Added automatic closing of handles on `CurlFactory` object destruction
## 7.7.1 - 2023-08-27
### Changed
- Remove the need for `AllowDynamicProperties` in `CurlMultiHandler`
## 7.7.0 - 2023-05-21
### Added
- Support `guzzlehttp/promises` v2
## 7.6.1 - 2023-05-15
### Fixed
- Fix `SetCookie::fromString` MaxAge deprecation warning and skip invalid MaxAge values
## 7.6.0 - 2023-05-14
### Added
- Support for setting the minimum TLS version in a unified way
- Apply on request the version set in options parameters
## 7.5.2 - 2023-05-14
### Fixed
- Fixed set cookie constructor validation
- Fixed handling of files with `'0'` body
### Changed
- Corrected docs and default connect timeout value to 300 seconds
## 7.5.1 - 2023-04-17
### Fixed
- Fixed `NO_PROXY` settings so that setting the `proxy` option to `no` overrides the env variable
### Changed
- Adjusted `guzzlehttp/psr7` version constraint to `^1.9.1 || ^2.4.5`
## 7.5.0 - 2022-08-28 ## 7.5.0 - 2022-08-28
### Added ### Added
@ -9,20 +78,30 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Support PHP 8.2 - Support PHP 8.2
- Add request to delay closure params - Add request to delay closure params
## 7.4.5 - 2022-06-20 ## 7.4.5 - 2022-06-20
### Fixed
* Fix change in port should be considered a change in origin * Fix change in port should be considered a change in origin
* Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin * Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin
## 7.4.4 - 2022-06-09 ## 7.4.4 - 2022-06-09
### Fixed
* Fix failure to strip Authorization header on HTTP downgrade * Fix failure to strip Authorization header on HTTP downgrade
* Fix failure to strip the Cookie header on change in host or HTTP downgrade * Fix failure to strip the Cookie header on change in host or HTTP downgrade
## 7.4.3 - 2022-05-25 ## 7.4.3 - 2022-05-25
### Fixed
* Fix cross-domain cookie leakage * Fix cross-domain cookie leakage
## 7.4.2 - 2022-03-20 ## 7.4.2 - 2022-03-20
### Fixed ### Fixed
@ -31,6 +110,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Reject non-HTTP schemes in StreamHandler - Reject non-HTTP schemes in StreamHandler
- Set a default ssl.peer_name context in StreamHandler to allow `force_ip_resolve` - Set a default ssl.peer_name context in StreamHandler to allow `force_ip_resolve`
## 7.4.1 - 2021-12-06 ## 7.4.1 - 2021-12-06
### Changed ### Changed
@ -42,6 +122,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Only close curl handle if it's done [#2950](https://github.com/guzzle/guzzle/pull/2950) - Only close curl handle if it's done [#2950](https://github.com/guzzle/guzzle/pull/2950)
## 7.4.0 - 2021-10-18 ## 7.4.0 - 2021-10-18
### Added ### Added
@ -59,6 +140,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Be more strict with types [#2914](https://github.com/guzzle/guzzle/pull/2914), [#2917](https://github.com/guzzle/guzzle/pull/2917), [#2919](https://github.com/guzzle/guzzle/pull/2919), [#2945](https://github.com/guzzle/guzzle/pull/2945) - Be more strict with types [#2914](https://github.com/guzzle/guzzle/pull/2914), [#2917](https://github.com/guzzle/guzzle/pull/2917), [#2919](https://github.com/guzzle/guzzle/pull/2919), [#2945](https://github.com/guzzle/guzzle/pull/2945)
## 7.3.0 - 2021-03-23 ## 7.3.0 - 2021-03-23
### Added ### Added
@ -71,6 +153,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872) - Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872)
## 7.2.0 - 2020-10-10 ## 7.2.0 - 2020-10-10
### Added ### Added
@ -93,6 +176,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- Using environment variable GUZZLE_CURL_SELECT_TIMEOUT [#2786](https://github.com/guzzle/guzzle/pull/2786) - Using environment variable GUZZLE_CURL_SELECT_TIMEOUT [#2786](https://github.com/guzzle/guzzle/pull/2786)
## 7.1.1 - 2020-09-30 ## 7.1.1 - 2020-09-30
### Fixed ### Fixed
@ -104,6 +188,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- We dont connect curl `sink` on HEAD requests. - We dont connect curl `sink` on HEAD requests.
- Removed some PHP 5 workarounds - Removed some PHP 5 workarounds
## 7.1.0 - 2020-09-22 ## 7.1.0 - 2020-09-22
### Added ### Added
@ -126,14 +211,17 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
- `Utils::defaultCaBundle()` - `Utils::defaultCaBundle()`
- `CurlFactory::LOW_CURL_VERSION_NUMBER` - `CurlFactory::LOW_CURL_VERSION_NUMBER`
## 7.0.1 - 2020-06-27 ## 7.0.1 - 2020-06-27
* Fix multiply defined functions fatal error [#2699](https://github.com/guzzle/guzzle/pull/2699) * Fix multiply defined functions fatal error [#2699](https://github.com/guzzle/guzzle/pull/2699)
## 7.0.0 - 2020-06-27 ## 7.0.0 - 2020-06-27
No changes since 7.0.0-rc1. No changes since 7.0.0-rc1.
## 7.0.0-rc1 - 2020-06-15 ## 7.0.0-rc1 - 2020-06-15
### Changed ### Changed
@ -141,6 +229,7 @@ No changes since 7.0.0-rc1.
* Use error level for logging errors in Middleware [#2629](https://github.com/guzzle/guzzle/pull/2629) * Use error level for logging errors in Middleware [#2629](https://github.com/guzzle/guzzle/pull/2629)
* Disabled IDN support by default and require ext-intl to use it [#2675](https://github.com/guzzle/guzzle/pull/2675) * Disabled IDN support by default and require ext-intl to use it [#2675](https://github.com/guzzle/guzzle/pull/2675)
## 7.0.0-beta2 - 2020-05-25 ## 7.0.0-beta2 - 2020-05-25
### Added ### Added
@ -166,6 +255,7 @@ No changes since 7.0.0-rc1.
* Pool option `pool_size` [#2528](https://github.com/guzzle/guzzle/pull/2528) * Pool option `pool_size` [#2528](https://github.com/guzzle/guzzle/pull/2528)
## 7.0.0-beta1 - 2019-12-30 ## 7.0.0-beta1 - 2019-12-30
The diff might look very big but 95% of Guzzle users will be able to upgrade without modification. The diff might look very big but 95% of Guzzle users will be able to upgrade without modification.
@ -199,15 +289,18 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* `uri_template()` and `UriTemplate` [#2440](https://github.com/guzzle/guzzle/pull/2440) * `uri_template()` and `UriTemplate` [#2440](https://github.com/guzzle/guzzle/pull/2440)
* Request options `save_to` and `exceptions` [#2464](https://github.com/guzzle/guzzle/pull/2464) * Request options `save_to` and `exceptions` [#2464](https://github.com/guzzle/guzzle/pull/2464)
## 6.5.2 - 2019-12-23 ## 6.5.2 - 2019-12-23
* idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489) * idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489)
## 6.5.1 - 2019-12-21 ## 6.5.1 - 2019-12-21
* Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454) * Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454)
* IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424) * IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424)
## 6.5.0 - 2019-12-07 ## 6.5.0 - 2019-12-07
* Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143) * Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143)
@ -217,11 +310,13 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348) * Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348)
* Deprecated `ClientInterface::VERSION` * Deprecated `ClientInterface::VERSION`
## 6.4.1 - 2019-10-23 ## 6.4.1 - 2019-10-23
* No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that * No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that
* Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar` * Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar`
## 6.4.0 - 2019-10-23 ## 6.4.0 - 2019-10-23
* Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108) * Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108)
@ -234,6 +329,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335) * Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335)
* Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362) * Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362)
## 6.3.3 - 2018-04-22 ## 6.3.3 - 2018-04-22
* Fix: Default headers when decode_content is specified * Fix: Default headers when decode_content is specified
@ -275,13 +371,14 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684) * Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
* Improvement: Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827) * Improvement: Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
+ Minor code cleanups, documentation fixes and clarifications. + Minor code cleanups, documentation fixes and clarifications.
## 6.2.3 - 2017-02-28 ## 6.2.3 - 2017-02-28
* Fix deprecations with guzzle/psr7 version 1.4 * Fix deprecations with guzzle/psr7 version 1.4
## 6.2.2 - 2016-10-08 ## 6.2.2 - 2016-10-08
* Allow to pass nullable Response to delay callable * Allow to pass nullable Response to delay callable
@ -289,6 +386,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Fix drain case where content-length is the literal string zero * Fix drain case where content-length is the literal string zero
* Obfuscate in-URL credentials in exceptions * Obfuscate in-URL credentials in exceptions
## 6.2.1 - 2016-07-18 ## 6.2.1 - 2016-07-18
* Address HTTP_PROXY security vulnerability, CVE-2016-5385: * Address HTTP_PROXY security vulnerability, CVE-2016-5385:
@ -299,6 +397,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
a server does not honor `Connection: close`. a server does not honor `Connection: close`.
* Ignore URI fragment when sending requests. * Ignore URI fragment when sending requests.
## 6.2.0 - 2016-03-21 ## 6.2.0 - 2016-03-21
* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`. * Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
@ -318,6 +417,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Bug fix: provide an empty string to `http_build_query` for HHVM workaround. * Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
https://github.com/guzzle/guzzle/pull/1367 https://github.com/guzzle/guzzle/pull/1367
## 6.1.1 - 2015-11-22 ## 6.1.1 - 2015-11-22
* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler * Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
@ -333,6 +433,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Bug fix: fixed regression where MockHandler was not using `sink`. * Bug fix: fixed regression where MockHandler was not using `sink`.
https://github.com/guzzle/guzzle/pull/1292 https://github.com/guzzle/guzzle/pull/1292
## 6.1.0 - 2015-09-08 ## 6.1.0 - 2015-09-08
* Feature: Added the `on_stats` request option to provide access to transfer * Feature: Added the `on_stats` request option to provide access to transfer
@ -367,6 +468,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set. * Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
https://github.com/guzzle/guzzle/pull/1189 https://github.com/guzzle/guzzle/pull/1189
## 6.0.2 - 2015-07-04 ## 6.0.2 - 2015-07-04
* Fixed a memory leak in the curl handlers in which references to callbacks * Fixed a memory leak in the curl handlers in which references to callbacks
@ -384,6 +486,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Functions are now conditionally required using an additional level of * Functions are now conditionally required using an additional level of
indirection to help with global Composer installations. indirection to help with global Composer installations.
## 6.0.1 - 2015-05-27 ## 6.0.1 - 2015-05-27
* Fixed a bug with serializing the `query` request option where the `&` * Fixed a bug with serializing the `query` request option where the `&`
@ -392,6 +495,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
use `form_params` or `multipart` instead. use `form_params` or `multipart` instead.
* Various doc fixes. * Various doc fixes.
## 6.0.0 - 2015-05-26 ## 6.0.0 - 2015-05-26
* See the UPGRADING.md document for more information. * See the UPGRADING.md document for more information.
@ -416,6 +520,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* `$maxHandles` has been removed from CurlMultiHandler. * `$maxHandles` has been removed from CurlMultiHandler.
* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package. * `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
## 5.3.0 - 2015-05-19 ## 5.3.0 - 2015-05-19
* Mock now supports `save_to` * Mock now supports `save_to`
@ -426,6 +531,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. * Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
* URL scheme is now always lowercased. * URL scheme is now always lowercased.
## 6.0.0-beta.1 ## 6.0.0-beta.1
* Requires PHP >= 5.5 * Requires PHP >= 5.5
@ -478,6 +584,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* `GuzzleHttp\QueryParser` has been replaced with the * `GuzzleHttp\QueryParser` has been replaced with the
`GuzzleHttp\Psr7\parse_query`. `GuzzleHttp\Psr7\parse_query`.
## 5.2.0 - 2015-01-27 ## 5.2.0 - 2015-01-27
* Added `AppliesHeadersInterface` to make applying headers to a request based * Added `AppliesHeadersInterface` to make applying headers to a request based
@ -488,6 +595,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
RingBridge. RingBridge.
* Added a guard in the Pool class to not use recursion for request retries. * Added a guard in the Pool class to not use recursion for request retries.
## 5.1.0 - 2014-12-19 ## 5.1.0 - 2014-12-19
* Pool class no longer uses recursion when a request is intercepted. * Pool class no longer uses recursion when a request is intercepted.
@ -508,6 +616,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle * Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
specific exceptions if necessary. specific exceptions if necessary.
## 5.0.3 - 2014-11-03 ## 5.0.3 - 2014-11-03
This change updates query strings so that they are treated as un-encoded values This change updates query strings so that they are treated as un-encoded values
@ -522,6 +631,7 @@ string that should not be parsed or encoded (unless a call to getQuery() is
subsequently made, forcing the query-string to be converted into a Query subsequently made, forcing the query-string to be converted into a Query
object). object).
## 5.0.2 - 2014-10-30 ## 5.0.2 - 2014-10-30
* Added a trailing `\r\n` to multipart/form-data payloads. See * Added a trailing `\r\n` to multipart/form-data payloads. See
@ -541,7 +651,9 @@ object).
* Note: This has been changed in 5.0.3 to now encode query string values by * Note: This has been changed in 5.0.3 to now encode query string values by
default unless the `rawString` argument is provided when setting the query default unless the `rawString` argument is provided when setting the query
string on a URL: Now allowing many more characters to be present in the string on a URL: Now allowing many more characters to be present in the
query string without being percent encoded. See https://tools.ietf.org/html/rfc3986#appendix-A query string without being percent encoded. See
https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
## 5.0.1 - 2014-10-16 ## 5.0.1 - 2014-10-16
@ -554,6 +666,7 @@ Bugfix release.
* Fixed an issue where transfer statistics were not being populated in the * Fixed an issue where transfer statistics were not being populated in the
RingBridge. https://github.com/guzzle/guzzle/issues/866 RingBridge. https://github.com/guzzle/guzzle/issues/866
## 5.0.0 - 2014-10-12 ## 5.0.0 - 2014-10-12
Adding support for non-blocking responses and some minor API cleanup. Adding support for non-blocking responses and some minor API cleanup.
@ -635,6 +748,7 @@ interfaces.
argument. They now accept an associative array of options, including the argument. They now accept an associative array of options, including the
"size" key and "metadata" key which can be used to provide custom metadata. "size" key and "metadata" key which can be used to provide custom metadata.
## 4.2.2 - 2014-09-08 ## 4.2.2 - 2014-09-08
* Fixed a memory leak in the CurlAdapter when reusing cURL handles. * Fixed a memory leak in the CurlAdapter when reusing cURL handles.
@ -1077,7 +1191,7 @@ interfaces.
## 3.4.0 - 2013-04-11 ## 3.4.0 - 2013-04-11
* Bug fix: URLs are now resolved correctly based on https://tools.ietf.org/html/rfc3986#section-5.2. #289 * Bug fix: URLs are now resolved correctly based on https://datatracker.ietf.org/doc/html/rfc3986#section-5.2. #289
* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 * Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 * Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. * Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.

View File

@ -3,7 +3,7 @@
# Guzzle, PHP HTTP client # Guzzle, PHP HTTP client
[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases) [![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI) [![Build Status](https://img.shields.io/github/actions/workflow/status/guzzle/guzzle/ci.yml?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle) [![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
@ -61,12 +61,12 @@ composer require guzzlehttp/guzzle
## Version Guidance ## Version Guidance
| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version | | Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version |
|---------|----------------|---------------------|--------------|---------------------|---------------------|-------|--------------| |---------|---------------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >=5.3.3,<7.0 | | 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >=5.3.3,<7.0 |
| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 | | 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 |
| 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 | | 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 |
| 6.x | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 | | 6.x | Security fixes only | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 |
| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.2 | | 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.4 |
[guzzle-3-repo]: https://github.com/guzzle/guzzle3 [guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x

View File

@ -27,7 +27,7 @@ Please make sure:
- Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed. - Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed.
Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative. Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative.
- Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed. - Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed.
- Request option `exception` is removed. Please use `http_errors`. - Request option `exceptions` is removed. Please use `http_errors`.
- Request option `save_to` is removed. Please use `sink`. - Request option `save_to` is removed. Please use `sink`.
- Pool option `pool_size` is removed. Please use `concurrency`. - Pool option `pool_size` is removed. Please use `concurrency`.
- We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility. - We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility.
@ -189,11 +189,11 @@ $client = new GuzzleHttp\Client(['handler' => $handler]);
## POST Requests ## POST Requests
This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params) This version added the [`form_params`](https://docs.guzzlephp.org/en/latest/request-options.html#form_params)
and `multipart` request options. `form_params` is an associative array of and `multipart` request options. `form_params` is an associative array of
strings or array of strings and is used to serialize an strings or array of strings and is used to serialize an
`application/x-www-form-urlencoded` POST request. The `application/x-www-form-urlencoded` POST request. The
[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart) [`multipart`](https://docs.guzzlephp.org/en/latest/request-options.html#multipart)
option is now used to send a multipart/form-data POST request. option is now used to send a multipart/form-data POST request.
`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add `GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
@ -209,7 +209,7 @@ The `base_url` option has been renamed to `base_uri`.
## Rewritten Adapter Layer ## Rewritten Adapter Layer
Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send Guzzle now uses [RingPHP](https://ringphp.readthedocs.org/en/latest) to send
HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
is still supported, but it has now been renamed to `handler`. Instead of is still supported, but it has now been renamed to `handler`. Instead of
passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
@ -575,7 +575,7 @@ You can intercept a request and inject a response using the `intercept()` event
of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
`GuzzleHttp\Event\ErrorEvent` event. `GuzzleHttp\Event\ErrorEvent` event.
See: http://docs.guzzlephp.org/en/latest/events.html See: https://docs.guzzlephp.org/en/latest/events.html
## Inflection ## Inflection
@ -668,9 +668,9 @@ in separate repositories:
The service description layer of Guzzle has moved into two separate packages: The service description layer of Guzzle has moved into two separate packages:
- http://github.com/guzzle/command Provides a high level abstraction over web - https://github.com/guzzle/command Provides a high level abstraction over web
services by representing web service operations using commands. services by representing web service operations using commands.
- http://github.com/guzzle/guzzle-services Provides an implementation of - https://github.com/guzzle/guzzle-services Provides an implementation of
guzzle/command that provides request serialization and response parsing using guzzle/command that provides request serialization and response parsing using
Guzzle service descriptions. Guzzle service descriptions.
@ -870,7 +870,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc.).
3.3 to 3.4 3.3 to 3.4
---------- ----------
Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. Base URLs of a client now follow the rules of https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2 when merging URLs.
3.2 to 3.3 3.2 to 3.3
---------- ----------

View File

@ -53,8 +53,8 @@
"require": { "require": {
"php": "^7.2.5 || ^8.0", "php": "^7.2.5 || ^8.0",
"ext-json": "*", "ext-json": "*",
"guzzlehttp/promises": "^1.5", "guzzlehttp/promises": "^1.5.3 || ^2.0.1",
"guzzlehttp/psr7": "^1.9 || ^2.4", "guzzlehttp/psr7": "^1.9.1 || ^2.5.1",
"psr/http-client": "^1.0", "psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0" "symfony/deprecation-contracts": "^2.2 || ^3.0"
}, },
@ -63,9 +63,10 @@
}, },
"require-dev": { "require-dev": {
"ext-curl": "*", "ext-curl": "*",
"bamarni/composer-bin-plugin": "^1.8.1", "bamarni/composer-bin-plugin": "^1.8.2",
"php-http/client-integration-tests": "^3.0", "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"phpunit/phpunit": "^8.5.29 || ^9.5.23", "php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.36 || ^9.6.15",
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
}, },
"suggest": { "suggest": {
@ -84,9 +85,6 @@
"bamarni-bin": { "bamarni-bin": {
"bin-links": true, "bin-links": true,
"forward-command": false "forward-command": false
},
"branch-alias": {
"dev-master": "7.5-dev"
} }
}, },
"autoload": { "autoload": {

View File

@ -120,13 +120,14 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
public function send(RequestInterface $request, array $options = []): ResponseInterface public function send(RequestInterface $request, array $options = []): ResponseInterface
{ {
$options[RequestOptions::SYNCHRONOUS] = true; $options[RequestOptions::SYNCHRONOUS] = true;
return $this->sendAsync($request, $options)->wait(); return $this->sendAsync($request, $options)->wait();
} }
/** /**
* The HttpClient PSR (PSR-18) specify this method. * The HttpClient PSR (PSR-18) specify this method.
* *
* @inheritDoc * {@inheritDoc}
*/ */
public function sendRequest(RequestInterface $request): ResponseInterface public function sendRequest(RequestInterface $request): ResponseInterface
{ {
@ -184,6 +185,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
public function request(string $method, $uri = '', array $options = []): ResponseInterface public function request(string $method, $uri = '', array $options = []): ResponseInterface
{ {
$options[RequestOptions::SYNCHRONOUS] = true; $options[RequestOptions::SYNCHRONOUS] = true;
return $this->requestAsync($method, $uri, $options)->wait(); return $this->requestAsync($method, $uri, $options)->wait();
} }
@ -200,7 +202,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
* *
* @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0.
*/ */
public function getConfig(?string $option = null) public function getConfig(string $option = null)
{ {
return $option === null return $option === null
? $this->config ? $this->config
@ -437,6 +439,10 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
} }
} }
if (isset($options['version'])) {
$modify['version'] = $options['version'];
}
$request = Psr7\Utils::modifyRequest($request, $modify); $request = Psr7\Utils::modifyRequest($request, $modify);
if ($request->getBody() instanceof Psr7\MultipartStream) { if ($request->getBody() instanceof Psr7\MultipartStream) {
// Use a multipart/form-data POST if a Content-Type is not set. // Use a multipart/form-data POST if a Content-Type is not set.

View File

@ -80,5 +80,5 @@ interface ClientInterface
* *
* @deprecated ClientInterface::getConfig will be removed in guzzlehttp/guzzle:8.0. * @deprecated ClientInterface::getConfig will be removed in guzzlehttp/guzzle:8.0.
*/ */
public function getConfig(?string $option = null); public function getConfig(string $option = null);
} }

View File

@ -53,7 +53,7 @@ class CookieJar implements CookieJarInterface
'Domain' => $domain, 'Domain' => $domain,
'Name' => $name, 'Name' => $name,
'Value' => $value, 'Value' => $value,
'Discard' => true 'Discard' => true,
])); ]));
} }
@ -96,9 +96,6 @@ class CookieJar implements CookieJarInterface
return null; return null;
} }
/**
* @inheritDoc
*/
public function toArray(): array public function toArray(): array
{ {
return \array_map(static function (SetCookie $cookie): array { return \array_map(static function (SetCookie $cookie): array {
@ -106,13 +103,11 @@ class CookieJar implements CookieJarInterface
}, $this->getIterator()->getArrayCopy()); }, $this->getIterator()->getArrayCopy());
} }
/** public function clear(string $domain = null, string $path = null, string $name = null): void
* @inheritDoc
*/
public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
{ {
if (!$domain) { if (!$domain) {
$this->cookies = []; $this->cookies = [];
return; return;
} elseif (!$path) { } elseif (!$path) {
$this->cookies = \array_filter( $this->cookies = \array_filter(
@ -125,25 +120,22 @@ class CookieJar implements CookieJarInterface
$this->cookies = \array_filter( $this->cookies = \array_filter(
$this->cookies, $this->cookies,
static function (SetCookie $cookie) use ($path, $domain): bool { static function (SetCookie $cookie) use ($path, $domain): bool {
return !($cookie->matchesPath($path) && return !($cookie->matchesPath($path)
$cookie->matchesDomain($domain)); && $cookie->matchesDomain($domain));
} }
); );
} else { } else {
$this->cookies = \array_filter( $this->cookies = \array_filter(
$this->cookies, $this->cookies,
static function (SetCookie $cookie) use ($path, $domain, $name) { static function (SetCookie $cookie) use ($path, $domain, $name) {
return !($cookie->getName() == $name && return !($cookie->getName() == $name
$cookie->matchesPath($path) && && $cookie->matchesPath($path)
$cookie->matchesDomain($domain)); && $cookie->matchesDomain($domain));
} }
); );
} }
} }
/**
* @inheritDoc
*/
public function clearSessionCookies(): void public function clearSessionCookies(): void
{ {
$this->cookies = \array_filter( $this->cookies = \array_filter(
@ -154,9 +146,6 @@ class CookieJar implements CookieJarInterface
); );
} }
/**
* @inheritDoc
*/
public function setCookie(SetCookie $cookie): bool public function setCookie(SetCookie $cookie): bool
{ {
// If the name string is empty (but not 0), ignore the set-cookie // If the name string is empty (but not 0), ignore the set-cookie
@ -173,6 +162,7 @@ class CookieJar implements CookieJarInterface
throw new \RuntimeException('Invalid cookie: '.$result); throw new \RuntimeException('Invalid cookie: '.$result);
} }
$this->removeCookieIfEmpty($cookie); $this->removeCookieIfEmpty($cookie);
return false; return false;
} }
@ -180,9 +170,9 @@ class CookieJar implements CookieJarInterface
foreach ($this->cookies as $i => $c) { foreach ($this->cookies as $i => $c) {
// Two cookies are identical, when their path, and domain are // Two cookies are identical, when their path, and domain are
// identical. // identical.
if ($c->getPath() != $cookie->getPath() || if ($c->getPath() != $cookie->getPath()
$c->getDomain() != $cookie->getDomain() || || $c->getDomain() != $cookie->getDomain()
$c->getName() != $cookie->getName() || $c->getName() != $cookie->getName()
) { ) {
continue; continue;
} }
@ -253,7 +243,7 @@ class CookieJar implements CookieJarInterface
/** /**
* Computes cookie path following RFC 6265 section 5.1.4 * Computes cookie path following RFC 6265 section 5.1.4
* *
* @link https://tools.ietf.org/html/rfc6265#section-5.1.4 * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
*/ */
private function getCookiePathFromRequest(RequestInterface $request): string private function getCookiePathFromRequest(RequestInterface $request): string
{ {
@ -284,10 +274,10 @@ class CookieJar implements CookieJarInterface
$path = $uri->getPath() ?: '/'; $path = $uri->getPath() ?: '/';
foreach ($this->cookies as $cookie) { foreach ($this->cookies as $cookie) {
if ($cookie->matchesPath($path) && if ($cookie->matchesPath($path)
$cookie->matchesDomain($host) && && $cookie->matchesDomain($host)
!$cookie->isExpired() && && !$cookie->isExpired()
(!$cookie->getSecure() || $scheme === 'https') && (!$cookie->getSecure() || $scheme === 'https')
) { ) {
$values[] = $cookie->getName().'=' $values[] = $cookie->getName().'='
.$cookie->getValue(); .$cookie->getValue();

View File

@ -13,7 +13,8 @@ use Psr\Http\Message\ResponseInterface;
* necessary. Subclasses are also responsible for storing and retrieving * necessary. Subclasses are also responsible for storing and retrieving
* cookies from a file, database, etc. * cookies from a file, database, etc.
* *
* @link https://docs.python.org/2/library/cookielib.html Inspiration * @see https://docs.python.org/2/library/cookielib.html Inspiration
*
* @extends \IteratorAggregate<SetCookie> * @extends \IteratorAggregate<SetCookie>
*/ */
interface CookieJarInterface extends \Countable, \IteratorAggregate interface CookieJarInterface extends \Countable, \IteratorAggregate
@ -61,7 +62,7 @@ interface CookieJarInterface extends \Countable, \IteratorAggregate
* @param string|null $path Clears cookies matching a domain and path * @param string|null $path Clears cookies matching a domain and path
* @param string|null $name Clears cookies matching a domain, path, and name * @param string|null $name Clears cookies matching a domain, path, and name
*/ */
public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void; public function clear(string $domain = null, string $path = null, string $name = null): void;
/** /**
* Discard all sessions cookies. * Discard all sessions cookies.

View File

@ -71,7 +71,7 @@ class SessionCookieJar extends CookieJar
$this->setCookie(new SetCookie($cookie)); $this->setCookie(new SetCookie($cookie));
} }
} elseif (\strlen($data)) { } elseif (\strlen($data)) {
throw new \RuntimeException("Invalid cookie data"); throw new \RuntimeException('Invalid cookie data');
} }
} }
} }

View File

@ -19,7 +19,7 @@ class SetCookie
'Expires' => null, 'Expires' => null,
'Secure' => false, 'Secure' => false,
'Discard' => false, 'Discard' => false,
'HttpOnly' => false 'HttpOnly' => false,
]; ];
/** /**
@ -58,7 +58,13 @@ class SetCookie
} else { } else {
foreach (\array_keys(self::$defaults) as $search) { foreach (\array_keys(self::$defaults) as $search) {
if (!\strcasecmp($search, $key)) { if (!\strcasecmp($search, $key)) {
if ($search === 'Max-Age') {
if (is_numeric($value)) {
$data[$search] = (int) $value;
}
} else {
$data[$search] = $value; $data[$search] = $value;
}
continue 2; continue 2;
} }
} }
@ -74,13 +80,49 @@ class SetCookie
*/ */
public function __construct(array $data = []) public function __construct(array $data = [])
{ {
/** @var array|null $replaced will be null in case of replace error */ $this->data = self::$defaults;
$replaced = \array_replace(self::$defaults, $data);
if ($replaced === null) { if (isset($data['Name'])) {
throw new \InvalidArgumentException('Unable to replace the default values for the Cookie.'); $this->setName($data['Name']);
}
if (isset($data['Value'])) {
$this->setValue($data['Value']);
}
if (isset($data['Domain'])) {
$this->setDomain($data['Domain']);
}
if (isset($data['Path'])) {
$this->setPath($data['Path']);
}
if (isset($data['Max-Age'])) {
$this->setMaxAge($data['Max-Age']);
}
if (isset($data['Expires'])) {
$this->setExpires($data['Expires']);
}
if (isset($data['Secure'])) {
$this->setSecure($data['Secure']);
}
if (isset($data['Discard'])) {
$this->setDiscard($data['Discard']);
}
if (isset($data['HttpOnly'])) {
$this->setHttpOnly($data['HttpOnly']);
}
// Set the remaining values that don't have extra validation logic
foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) {
$this->data[$key] = $data[$key];
} }
$this->data = $replaced;
// Extract the Expires value and turn it into a UNIX timestamp if needed // Extract the Expires value and turn it into a UNIX timestamp if needed
if (!$this->getExpires() && $this->getMaxAge()) { if (!$this->getExpires() && $this->getMaxAge()) {
// Calculate the Expires date // Calculate the Expires date
@ -378,7 +420,7 @@ class SetCookie
} }
// Remove the leading '.' as per spec in RFC 6265. // Remove the leading '.' as per spec in RFC 6265.
// https://tools.ietf.org/html/rfc6265#section-5.2.3 // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
$cookieDomain = \ltrim(\strtolower($cookieDomain), '.'); $cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
$domain = \strtolower($domain); $domain = \strtolower($domain);
@ -389,7 +431,7 @@ class SetCookie
} }
// Matching the subdomain according to RFC 6265. // Matching the subdomain according to RFC 6265.
// https://tools.ietf.org/html/rfc6265#section-5.1.3 // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
if (\filter_var($domain, \FILTER_VALIDATE_IP)) { if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
return false; return false;
} }

View File

@ -51,7 +51,7 @@ class CurlFactory implements CurlFactoryInterface
unset($options['curl']['body_as_string']); unset($options['curl']['body_as_string']);
} }
$easy = new EasyHandle; $easy = new EasyHandle();
$easy->request = $request; $easy->request = $request;
$easy->options = $options; $easy->options = $options;
$conf = $this->getDefaultConf($easy); $conf = $this->getDefaultConf($easy);
@ -224,7 +224,7 @@ class CurlFactory implements CurlFactoryInterface
\CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
\CURLOPT_RETURNTRANSFER => false, \CURLOPT_RETURNTRANSFER => false,
\CURLOPT_HEADER => false, \CURLOPT_HEADER => false,
\CURLOPT_CONNECTTIMEOUT => 150, \CURLOPT_CONNECTTIMEOUT => 300,
]; ];
if (\defined('CURLOPT_PROTOCOLS')) { if (\defined('CURLOPT_PROTOCOLS')) {
@ -250,12 +250,13 @@ class CurlFactory implements CurlFactoryInterface
if ($size === null || $size > 0) { if ($size === null || $size > 0) {
$this->applyBody($easy->request, $easy->options, $conf); $this->applyBody($easy->request, $easy->options, $conf);
return; return;
} }
$method = $easy->request->getMethod(); $method = $easy->request->getMethod();
if ($method === 'PUT' || $method === 'POST') { if ($method === 'PUT' || $method === 'POST') {
// See https://tools.ietf.org/html/rfc7230#section-3.3.2 // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
if (!$easy->request->hasHeader('Content-Length')) { if (!$easy->request->hasHeader('Content-Length')) {
$conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
} }
@ -341,6 +342,7 @@ class CurlFactory implements CurlFactoryInterface
foreach (\array_keys($options['_headers']) as $key) { foreach (\array_keys($options['_headers']) as $key) {
if (!\strcasecmp($key, $name)) { if (!\strcasecmp($key, $name)) {
unset($options['_headers'][$key]); unset($options['_headers'][$key]);
return; return;
} }
} }
@ -365,11 +367,11 @@ class CurlFactory implements CurlFactoryInterface
// If it's a directory or a link to a directory use CURLOPT_CAPATH. // If it's a directory or a link to a directory use CURLOPT_CAPATH.
// If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
if ( if (
\is_dir($options['verify']) || \is_dir($options['verify'])
( || (
\is_link($options['verify']) === true && \is_link($options['verify']) === true
($verifyLink = \readlink($options['verify'])) !== false && && ($verifyLink = \readlink($options['verify'])) !== false
\is_dir($verifyLink) && \is_dir($verifyLink)
) )
) { ) {
$conf[\CURLOPT_CAPATH] = $options['verify']; $conf[\CURLOPT_CAPATH] = $options['verify'];
@ -443,13 +445,41 @@ class CurlFactory implements CurlFactoryInterface
$scheme = $easy->request->getUri()->getScheme(); $scheme = $easy->request->getUri()->getScheme();
if (isset($options['proxy'][$scheme])) { if (isset($options['proxy'][$scheme])) {
$host = $easy->request->getUri()->getHost(); $host = $easy->request->getUri()->getHost();
if (!isset($options['proxy']['no']) || !Utils::isHostInNoProxy($host, $options['proxy']['no'])) { if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
unset($conf[\CURLOPT_PROXY]);
} else {
$conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme];
} }
} }
} }
} }
if (isset($options['crypto_method'])) {
if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) {
if (!defined('CURL_SSLVERSION_TLSv1_0')) {
throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL');
}
$conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0;
} elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) {
if (!defined('CURL_SSLVERSION_TLSv1_1')) {
throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL');
}
$conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1;
} elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) {
if (!defined('CURL_SSLVERSION_TLSv1_2')) {
throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL');
}
$conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
} elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
if (!defined('CURL_SSLVERSION_TLSv1_3')) {
throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
}
$conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
} else {
throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
}
}
if (isset($options['cert'])) { if (isset($options['cert'])) {
$cert = $options['cert']; $cert = $options['cert'];
if (\is_array($cert)) { if (\is_array($cert)) {
@ -459,8 +489,8 @@ class CurlFactory implements CurlFactoryInterface
if (!\file_exists($cert)) { if (!\file_exists($cert)) {
throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); throw new \InvalidArgumentException("SSL certificate not found: {$cert}");
} }
# OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
# see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
$ext = pathinfo($cert, \PATHINFO_EXTENSION); $ext = pathinfo($cert, \PATHINFO_EXTENSION);
if (preg_match('#^(der|p12)$#i', $ext)) { if (preg_match('#^(der|p12)$#i', $ext)) {
$conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext); $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext);
@ -526,6 +556,7 @@ class CurlFactory implements CurlFactoryInterface
.'providing an error. The request would have been retried, ' .'providing an error. The request would have been retried, '
.'but attempting to rewind the request body failed. ' .'but attempting to rewind the request body failed. '
.'Exception: '.$e; .'Exception: '.$e;
return self::createRejection($easy, $ctx); return self::createRejection($easy, $ctx);
} }
@ -539,9 +570,10 @@ class CurlFactory implements CurlFactoryInterface
.'and subsequent retries resulted in the same error. Turn on ' .'and subsequent retries resulted in the same error. Turn on '
.'the debug option to see what went wrong. See ' .'the debug option to see what went wrong. See '
.'https://bugs.php.net/bug.php?id=47204 for more information.'; .'https://bugs.php.net/bug.php?id=47204 for more information.';
return self::createRejection($easy, $ctx); return self::createRejection($easy, $ctx);
} else { } else {
$easy->options['_curl_retries']++; ++$easy->options['_curl_retries'];
} }
return $handler($easy->request, $easy->options); return $handler($easy->request, $easy->options);
@ -571,6 +603,7 @@ class CurlFactory implements CurlFactoryInterface
$easy->createResponse(); $easy->createResponse();
} catch (\Exception $e) { } catch (\Exception $e) {
$easy->createResponseException = $e; $easy->createResponseException = $e;
return -1; return -1;
} }
if ($onHeaders !== null) { if ($onHeaders !== null) {
@ -580,6 +613,7 @@ class CurlFactory implements CurlFactoryInterface
// Associate the exception with the handle and trigger // Associate the exception with the handle and trigger
// a curl header write error by returning 0. // a curl header write error by returning 0.
$easy->onHeadersException = $e; $easy->onHeadersException = $e;
return -1; return -1;
} }
} }
@ -589,7 +623,16 @@ class CurlFactory implements CurlFactoryInterface
} else { } else {
$easy->headers[] = $value; $easy->headers[] = $value;
} }
return \strlen($h); return \strlen($h);
}; };
} }
public function __destruct()
{
foreach ($this->handles as $id => $handle) {
\curl_close($handle);
unset($this->handles[$id]);
}
}
} }

View File

@ -15,11 +15,8 @@ use Psr\Http\Message\RequestInterface;
* associative array of curl option constants mapping to values in the * associative array of curl option constants mapping to values in the
* **curl** key of the provided request options. * **curl** key of the provided request options.
* *
* @property resource|\CurlMultiHandle $_mh Internal use only. Lazy loaded multi-handle.
*
* @final * @final
*/ */
#[\AllowDynamicProperties]
class CurlMultiHandler class CurlMultiHandler
{ {
/** /**
@ -56,6 +53,9 @@ class CurlMultiHandler
*/ */
private $options = []; private $options = [];
/** @var resource|\CurlMultiHandle */
private $_mh;
/** /**
* This handler accepts the following options: * This handler accepts the following options:
* *
@ -79,6 +79,10 @@ class CurlMultiHandler
} }
$this->options = $options['options'] ?? []; $this->options = $options['options'] ?? [];
// unsetting the property forces the first access to go through
// __get().
unset($this->_mh);
} }
/** /**
@ -164,7 +168,8 @@ class CurlMultiHandler
\usleep(250); \usleep(250);
} }
while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM); while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
}
$this->processMessages(); $this->processMessages();
} }

View File

@ -14,9 +14,9 @@ final class HeaderProcessor
* *
* @param string[] $headers * @param string[] $headers
* *
* @throws \RuntimeException
*
* @return array{0:string, 1:int, 2:?string, 3:array} * @return array{0:string, 1:int, 2:?string, 3:array}
*
* @throws \RuntimeException
*/ */
public static function parseHeaders(array $headers): array public static function parseHeaders(array $headers): array
{ {

View File

@ -138,6 +138,7 @@ class MockHandler implements \Countable
if ($this->onRejected) { if ($this->onRejected) {
($this->onRejected)($reason); ($this->onRejected)($reason);
} }
return P\Create::rejectionFor($reason); return P\Create::rejectionFor($reason);
} }
); );

View File

@ -67,7 +67,7 @@ class StreamHandler
if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed
|| false !== \strpos($message, 'Connection refused') || false !== \strpos($message, 'Connection refused')
|| false !== \strpos($message, "couldn't connect to host") // error on HHVM || false !== \strpos($message, "couldn't connect to host") // error on HHVM
|| false !== \strpos($message, "connection attempt failed") || false !== \strpos($message, 'connection attempt failed')
) { ) {
$e = new ConnectException($e->getMessage(), $request, $e); $e = new ConnectException($e->getMessage(), $request, $e);
} else { } else {
@ -232,8 +232,9 @@ class StreamHandler
$errors[] = [ $errors[] = [
'message' => $msg, 'message' => $msg,
'file' => $file, 'file' => $file,
'line' => $line 'line' => $line,
]; ];
return true; return true;
}); });
@ -350,6 +351,7 @@ class StreamHandler
if (false === $records || !isset($records[0]['ip'])) { if (false === $records || !isset($records[0]['ip'])) {
throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
} }
return $uri->withHost($records[0]['ip']); return $uri->withHost($records[0]['ip']);
} }
if ('v6' === $options['force_ip_resolve']) { if ('v6' === $options['force_ip_resolve']) {
@ -357,6 +359,7 @@ class StreamHandler
if (false === $records || !isset($records[0]['ipv6'])) { if (false === $records || !isset($records[0]['ipv6'])) {
throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
} }
return $uri->withHost('['.$records[0]['ipv6'].']'); return $uri->withHost('['.$records[0]['ipv6'].']');
} }
} }
@ -388,7 +391,7 @@ class StreamHandler
$body = (string) $request->getBody(); $body = (string) $request->getBody();
if (!empty($body)) { if ('' !== $body) {
$context['http']['content'] = $body; $context['http']['content'] = $body;
// Prevent the HTTP handler from adding a Content-Type header. // Prevent the HTTP handler from adding a Content-Type header.
if (!$request->hasHeader('Content-Type')) { if (!$request->hasHeader('Content-Type')) {
@ -472,6 +475,25 @@ class StreamHandler
} }
} }
/**
* @param mixed $value as passed via Request transfer options.
*/
private function add_crypto_method(RequestInterface $request, array &$options, $value, array &$params): void
{
if (
$value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
|| $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
|| $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
|| (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT)
) {
$options['http']['crypto_method'] = $value;
return;
}
throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
}
/** /**
* @param mixed $value as passed via Request transfer options. * @param mixed $value as passed via Request transfer options.
*/ */
@ -577,7 +599,7 @@ class StreamHandler
} else { } else {
$params['notification'] = self::callArray([ $params['notification'] = self::callArray([
$params['notification'], $params['notification'],
$notify $notify,
]); ]);
} }
} }

View File

@ -44,7 +44,7 @@ class HandlerStack
* handler is provided, the best handler for your * handler is provided, the best handler for your
* system will be utilized. * system will be utilized.
*/ */
public static function create(?callable $handler = null): self public static function create(callable $handler = null): self
{ {
$stack = new self($handler ?: Utils::chooseHandler()); $stack = new self($handler ?: Utils::chooseHandler());
$stack->push(Middleware::httpErrors(), 'http_errors'); $stack->push(Middleware::httpErrors(), 'http_errors');
@ -86,14 +86,14 @@ class HandlerStack
$stack = []; $stack = [];
if ($this->handler !== null) { if ($this->handler !== null) {
$stack[] = "0) Handler: " . $this->debugCallable($this->handler); $stack[] = '0) Handler: '.$this->debugCallable($this->handler);
} }
$result = ''; $result = '';
foreach (\array_reverse($this->stack) as $tuple) { foreach (\array_reverse($this->stack) as $tuple) {
$depth++; ++$depth;
$str = "{$depth}) Name: '{$tuple[1]}', "; $str = "{$depth}) Name: '{$tuple[1]}', ";
$str .= "Function: " . $this->debugCallable($tuple[0]); $str .= 'Function: '.$this->debugCallable($tuple[0]);
$result = "> {$str}\n{$result}"; $result = "> {$str}\n{$result}";
$stack[] = $str; $stack[] = $str;
} }
@ -131,7 +131,7 @@ class HandlerStack
* @param callable(callable): callable $middleware Middleware function * @param callable(callable): callable $middleware Middleware function
* @param string $name Name to register for this middleware. * @param string $name Name to register for this middleware.
*/ */
public function unshift(callable $middleware, ?string $name = null): void public function unshift(callable $middleware, string $name = null): void
{ {
\array_unshift($this->stack, [$middleware, $name]); \array_unshift($this->stack, [$middleware, $name]);
$this->cached = null; $this->cached = null;

View File

@ -40,11 +40,11 @@ class MessageFormatter implements MessageFormatterInterface
/** /**
* Apache Common Log Format. * Apache Common Log Format.
* *
* @link https://httpd.apache.org/docs/2.4/logs.html#common * @see https://httpd.apache.org/docs/2.4/logs.html#common
* *
* @var string * @var string
*/ */
public const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}"; public const CLF = '{hostname} {req_header_User-Agent} - [{date_common_log}] "{method} {target} HTTP/{version}" {code} {res_header_Content-Length}';
public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
@ -68,7 +68,7 @@ class MessageFormatter implements MessageFormatterInterface
* @param ResponseInterface|null $response Response that was received * @param ResponseInterface|null $response Response that was received
* @param \Throwable|null $error Exception that was received * @param \Throwable|null $error Exception that was received
*/ */
public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string
{ {
$cache = []; $cache = [];
@ -177,6 +177,7 @@ class MessageFormatter implements MessageFormatterInterface
} }
$cache[$matches[1]] = $result; $cache[$matches[1]] = $result;
return $result; return $result;
}, },
$this->template $this->template

View File

@ -14,5 +14,5 @@ interface MessageFormatterInterface
* @param ResponseInterface|null $response Response that was received * @param ResponseInterface|null $response Response that was received
* @param \Throwable|null $error Exception that was received * @param \Throwable|null $error Exception that was received
*/ */
public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string; public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string;
} }

View File

@ -34,10 +34,12 @@ final class Middleware
} }
$cookieJar = $options['cookies']; $cookieJar = $options['cookies'];
$request = $cookieJar->withCookieHeader($request); $request = $cookieJar->withCookieHeader($request);
return $handler($request, $options) return $handler($request, $options)
->then( ->then(
static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface { static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
$cookieJar->extractCookies($request, $response); $cookieJar->extractCookies($request, $response);
return $response; return $response;
} }
); );
@ -60,6 +62,7 @@ final class Middleware
if (empty($options['http_errors'])) { if (empty($options['http_errors'])) {
return $handler($request, $options); return $handler($request, $options);
} }
return $handler($request, $options)->then( return $handler($request, $options)->then(
static function (ResponseInterface $response) use ($request, $bodySummarizer) { static function (ResponseInterface $response) use ($request, $bodySummarizer) {
$code = $response->getStatusCode(); $code = $response->getStatusCode();
@ -96,8 +99,9 @@ final class Middleware
'request' => $request, 'request' => $request,
'response' => $value, 'response' => $value,
'error' => null, 'error' => null,
'options' => $options 'options' => $options,
]; ];
return $value; return $value;
}, },
static function ($reason) use ($request, &$container, $options) { static function ($reason) use ($request, &$container, $options) {
@ -105,8 +109,9 @@ final class Middleware
'request' => $request, 'request' => $request,
'response' => null, 'response' => null,
'error' => $reason, 'error' => $reason,
'options' => $options 'options' => $options,
]; ];
return P\Create::rejectionFor($reason); return P\Create::rejectionFor($reason);
} }
); );
@ -138,6 +143,7 @@ final class Middleware
if ($after) { if ($after) {
$after($request, $options, $response); $after($request, $options, $response);
} }
return $response; return $response;
}; };
}; };
@ -202,12 +208,14 @@ final class Middleware
static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface { static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface {
$message = $formatter->format($request, $response); $message = $formatter->format($request, $response);
$logger->log($logLevel, $message); $logger->log($logLevel, $message);
return $response; return $response;
}, },
static function ($reason) use ($logger, $request, $formatter): PromiseInterface { static function ($reason) use ($logger, $request, $formatter): PromiseInterface {
$response = $reason instanceof RequestException ? $reason->getResponse() : null; $response = $reason instanceof RequestException ? $reason->getResponse() : null;
$message = $formatter->format($request, $response, P\Create::exceptionFor($reason)); $message = $formatter->format($request, $response, P\Create::exceptionFor($reason));
$logger->error($message); $logger->error($message);
return P\Create::rejectionFor($reason); return P\Create::rejectionFor($reason);
} }
); );

View File

@ -84,6 +84,7 @@ class PrepareBodyMiddleware
// The expect header is unconditionally enabled // The expect header is unconditionally enabled
if ($expect === true) { if ($expect === true) {
$modify['set_headers']['Expect'] = '100-Continue'; $modify['set_headers']['Expect'] = '100-Continue';
return; return;
} }

View File

@ -166,8 +166,8 @@ class RedirectMiddleware
// not forcing RFC compliance, but rather emulating what all browsers // not forcing RFC compliance, but rather emulating what all browsers
// would do. // would do.
$statusCode = $response->getStatusCode(); $statusCode = $response->getStatusCode();
if ($statusCode == 303 || if ($statusCode == 303
($statusCode <= 302 && !$options['allow_redirects']['strict']) || ($statusCode <= 302 && !$options['allow_redirects']['strict'])
) { ) {
$safeMethods = ['GET', 'HEAD', 'OPTIONS']; $safeMethods = ['GET', 'HEAD', 'OPTIONS'];
$requestMethod = $request->getMethod(); $requestMethod = $request->getMethod();

View File

@ -5,9 +5,7 @@ namespace GuzzleHttp;
/** /**
* This class contains a list of built-in Guzzle request options. * This class contains a list of built-in Guzzle request options.
* *
* More documentation for each option can be found at http://guzzlephp.org/. * @see https://docs.guzzlephp.org/en/latest/request-options.html
*
* @link http://docs.guzzlephp.org/en/v6/request-options.html
*/ */
final class RequestOptions final class RequestOptions
{ {
@ -70,10 +68,22 @@ final class RequestOptions
/** /**
* connect_timeout: (float, default=0) Float describing the number of * connect_timeout: (float, default=0) Float describing the number of
* seconds to wait while trying to connect to a server. Use 0 to wait * seconds to wait while trying to connect to a server. Use 0 to wait
* indefinitely (the default behavior). * 300 seconds (the default behavior).
*/ */
public const CONNECT_TIMEOUT = 'connect_timeout'; public const CONNECT_TIMEOUT = 'connect_timeout';
/**
* crypto_method: (int) A value describing the minimum TLS protocol
* version to use.
*
* This setting must be set to one of the
* ``STREAM_CRYPTO_METHOD_TLS*_CLIENT`` constants. PHP 7.4 or higher is
* required in order to use TLS 1.3, and cURL 7.34.0 or higher is required
* in order to specify a crypto method, with cURL 7.52.0 or higher being
* required to use TLS 1.3.
*/
public const CRYPTO_METHOD = 'crypto_method';
/** /**
* debug: (bool|resource) Set to true or set to a PHP stream returned by * debug: (bool|resource) Set to true or set to a PHP stream returned by
* fopen() enable debug output with the HTTP handler used to send a * fopen() enable debug output with the HTTP handler used to send a

View File

@ -54,7 +54,7 @@ class RetryMiddleware
*/ */
public static function exponentialDelay(int $retries): int public static function exponentialDelay(int $retries): int
{ {
return (int) \pow(2, $retries - 1) * 1000; return (int) 2 ** ($retries - 1) * 1000;
} }
public function __invoke(RequestInterface $request, array $options): PromiseInterface public function __invoke(RequestInterface $request, array $options): PromiseInterface
@ -64,6 +64,7 @@ class RetryMiddleware
} }
$fn = $this->nextHandler; $fn = $this->nextHandler;
return $fn($request, $options) return $fn($request, $options)
->then( ->then(
$this->onFulfilled($request, $options), $this->onFulfilled($request, $options),
@ -85,6 +86,7 @@ class RetryMiddleware
)) { )) {
return $value; return $value;
} }
return $this->doRetry($request, $options, $value); return $this->doRetry($request, $options, $value);
}; };
} }
@ -103,6 +105,7 @@ class RetryMiddleware
)) { )) {
return P\Create::rejectionFor($reason); return P\Create::rejectionFor($reason);
} }
return $this->doRetry($req, $options); return $this->doRetry($req, $options);
}; };
} }

View File

@ -46,8 +46,8 @@ final class TransferStats
*/ */
public function __construct( public function __construct(
RequestInterface $request, RequestInterface $request,
?ResponseInterface $response = null, ResponseInterface $response = null,
?float $transferTime = null, float $transferTime = null,
$handlerErrorData = null, $handlerErrorData = null,
array $handlerStats = [] array $handlerStats = []
) { ) {

View File

@ -79,9 +79,9 @@ final class Utils
* *
* The returned handler is not wrapped by any default middlewares. * The returned handler is not wrapped by any default middlewares.
* *
* @throws \RuntimeException if no viable Handler is available.
*
* @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system. * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
*
* @throws \RuntimeException if no viable Handler is available.
*/ */
public static function chooseHandler(): callable public static function chooseHandler(): callable
{ {
@ -176,14 +176,13 @@ No system CA bundle could be found in any of the the common system locations.
PHP versions earlier than 5.6 are not properly configured to use the system's PHP versions earlier than 5.6 are not properly configured to use the system's
CA bundle by default. In order to verify peer certificates, you will need to CA bundle by default. In order to verify peer certificates, you will need to
supply the path on disk to a certificate bundle to the 'verify' request supply the path on disk to a certificate bundle to the 'verify' request
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If
need a specific certificate bundle, then Mozilla provides a commonly used CA you do not need a specific certificate bundle, then Mozilla provides a commonly
bundle which can be downloaded here (provided by the maintainer of cURL): used CA bundle which can be downloaded here (provided by the maintainer of
https://curl.haxx.se/ca/cacert.pem. Once cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
ini setting to point to the path to the file, allowing you to omit the 'verify' to the file, allowing you to omit the 'verify' request option. See
request option. See https://curl.haxx.se/docs/sslcerts.html for more https://curl.haxx.se/docs/sslcerts.html for more information.
information.
EOT EOT
); );
} }
@ -248,7 +247,7 @@ EOT
// Special match if the area when prefixed with ".". Remove any // Special match if the area when prefixed with ".". Remove any
// existing leading "." and add a new leading ".". // existing leading "." and add a new leading ".".
$area = '.'.\ltrim($area, '.'); $area = '.'.\ltrim($area, '.');
if (\substr($host, -(\strlen($area))) === $area) { if (\substr($host, -\strlen($area)) === $area) {
return true; return true;
} }
} }
@ -269,7 +268,7 @@ EOT
* *
* @throws InvalidArgumentException if the JSON cannot be decoded. * @throws InvalidArgumentException if the JSON cannot be decoded.
* *
* @link https://www.php.net/manual/en/function.json-decode.php * @see https://www.php.net/manual/en/function.json-decode.php
*/ */
public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
{ {
@ -290,7 +289,7 @@ EOT
* *
* @throws InvalidArgumentException if the JSON cannot be encoded. * @throws InvalidArgumentException if the JSON cannot be encoded.
* *
* @link https://www.php.net/manual/en/function.json-encode.php * @see https://www.php.net/manual/en/function.json-encode.php
*/ */
public static function jsonEncode($value, int $options = 0, int $depth = 512): string public static function jsonEncode($value, int $options = 0, int $depth = 512): string
{ {

View File

@ -50,10 +50,10 @@ function debug_resource($value = null)
* *
* The returned handler is not wrapped by any default middlewares. * The returned handler is not wrapped by any default middlewares.
* *
* @throws \RuntimeException if no viable Handler is available.
*
* @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system. * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
* *
* @throws \RuntimeException if no viable Handler is available.
*
* @deprecated choose_handler will be removed in guzzlehttp/guzzle:8.0. Use Utils::chooseHandler instead. * @deprecated choose_handler will be removed in guzzlehttp/guzzle:8.0. Use Utils::chooseHandler instead.
*/ */
function choose_handler(): callable function choose_handler(): callable
@ -141,7 +141,7 @@ function is_host_in_noproxy(string $host, array $noProxyArray): bool
* *
* @throws Exception\InvalidArgumentException if the JSON cannot be decoded. * @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
* *
* @link https://www.php.net/manual/en/function.json-decode.php * @see https://www.php.net/manual/en/function.json-decode.php
* @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead. * @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead.
*/ */
function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
@ -158,7 +158,7 @@ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $o
* *
* @throws Exception\InvalidArgumentException if the JSON cannot be encoded. * @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
* *
* @link https://www.php.net/manual/en/function.json-encode.php * @see https://www.php.net/manual/en/function.json-encode.php
* @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead. * @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead.
*/ */
function json_encode($value, int $options = 0, int $depth = 512): string function json_encode($value, int $options = 0, int $depth = 512): string

View File

@ -1,11 +1,50 @@
# CHANGELOG # CHANGELOG
## 2.0.2 - 2023-12-03
### Changed
- Replaced `call_user_func*` with native calls
## 2.0.1 - 2023-08-03
### Changed
- PHP 8.3 support
## 2.0.0 - 2023-05-21
### Added
- Added PHP 7 type hints
### Changed
- All previously non-final non-exception classes have been marked as soft-final
### Removed
- Dropped PHP < 7.2 support
- All functions in the `GuzzleHttp\Promise` namespace
## 1.5.3 - 2023-05-21
### Changed
- Removed remaining usage of deprecated functions
## 1.5.2 - 2022-08-07 ## 1.5.2 - 2022-08-07
### Changed ### Changed
- Officially support PHP 8.2 - Officially support PHP 8.2
## 1.5.1 - 2021-10-22 ## 1.5.1 - 2021-10-22
### Fixed ### Fixed
@ -13,6 +52,7 @@
- Revert "Call handler when waiting on fulfilled/rejected Promise" - Revert "Call handler when waiting on fulfilled/rejected Promise"
- Fix pool memory leak when empty array of promises provided - Fix pool memory leak when empty array of promises provided
## 1.5.0 - 2021-10-07 ## 1.5.0 - 2021-10-07
### Changed ### Changed
@ -24,12 +64,14 @@
- Fix manually settle promises generated with `Utils::task` - Fix manually settle promises generated with `Utils::task`
## 1.4.1 - 2021-02-18 ## 1.4.1 - 2021-02-18
### Fixed ### Fixed
- Fixed `each_limit` skipping promises and failing - Fixed `each_limit` skipping promises and failing
## 1.4.0 - 2020-09-30 ## 1.4.0 - 2020-09-30
### Added ### Added

View File

@ -29,6 +29,21 @@ for a general introduction to promises.
`GuzzleHttp\Promise\Coroutine::of()`. `GuzzleHttp\Promise\Coroutine::of()`.
## Installation
```shell
composer require guzzlehttp/promises
```
## Version Guidance
| Version | Status | PHP Version |
|---------|------------------------|--------------|
| 1.x | Bug and security fixes | >=5.5,<8.3 |
| 2.x | Latest | >=7.2.5,<8.4 |
## Quick Start ## Quick Start
A *promise* represents the eventual result of an asynchronous operation. The A *promise* represents the eventual result of an asynchronous operation. The
@ -430,8 +445,6 @@ $loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']); $loop->addPeriodicTimer(0, [$queue, 'run']);
``` ```
*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
## Implementation Notes ## Implementation Notes
@ -501,8 +514,8 @@ $promise->resolve('foo');
A static API was first introduced in 1.4.0, in order to mitigate problems with A static API was first introduced in 1.4.0, in order to mitigate problems with
functions conflicting between global and local copies of the package. The functions conflicting between global and local copies of the package. The
function API will be removed in 2.0.0. A migration table has been provided here function API was removed in 2.0.0. A migration table has been provided here for
for your convenience: your convenience:
| Original Function | Replacement Method | | Original Function | Replacement Method |
|----------------|----------------| |----------------|----------------|

View File

@ -26,32 +26,32 @@
} }
], ],
"require": { "require": {
"php": ">=5.5" "php": "^7.2.5 || ^8.0"
}, },
"require-dev": { "require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1" "bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.36 || ^9.6.15"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"GuzzleHttp\\Promise\\": "src/" "GuzzleHttp\\Promise\\": "src/"
}, }
"files": ["src/functions_include.php"]
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"GuzzleHttp\\Promise\\Tests\\": "tests/" "GuzzleHttp\\Promise\\Tests\\": "tests/"
} }
}, },
"scripts": {
"test": "vendor/bin/simple-phpunit",
"test-ci": "vendor/bin/simple-phpunit --coverage-text"
},
"extra": { "extra": {
"branch-alias": { "bamarni-bin": {
"dev-master": "1.5-dev" "bin-links": true,
"forward-command": false
} }
}, },
"config": { "config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true
},
"preferred-install": "dist", "preferred-install": "dist",
"sort-packages": true "sort-packages": true
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -7,7 +9,7 @@ namespace GuzzleHttp\Promise;
*/ */
class AggregateException extends RejectionException class AggregateException extends RejectionException
{ {
public function __construct($msg, array $reasons) public function __construct(string $msg, array $reasons)
{ {
parent::__construct( parent::__construct(
$reasons, $reasons,

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**

View File

@ -1,8 +1,9 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
use Exception;
use Generator; use Generator;
use Throwable; use Throwable;
@ -27,7 +28,7 @@ use Throwable;
* $value = (yield createPromise('a')); * $value = (yield createPromise('a'));
* try { * try {
* $value = (yield createPromise($value . 'b')); * $value = (yield createPromise($value . 'b'));
* } catch (\Exception $e) { * } catch (\Throwable $e) {
* // The promise was rejected. * // The promise was rejected.
* } * }
* yield $value . 'c'; * yield $value . 'c';
@ -40,7 +41,7 @@ use Throwable;
* *
* @return Promise * @return Promise
* *
* @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
*/ */
final class Coroutine implements PromiseInterface final class Coroutine implements PromiseInterface
{ {
@ -62,15 +63,13 @@ final class Coroutine implements PromiseInterface
public function __construct(callable $generatorFn) public function __construct(callable $generatorFn)
{ {
$this->generator = $generatorFn(); $this->generator = $generatorFn();
$this->result = new Promise(function () { $this->result = new Promise(function (): void {
while (isset($this->currentPromise)) { while (isset($this->currentPromise)) {
$this->currentPromise->wait(); $this->currentPromise->wait();
} }
}); });
try { try {
$this->nextCoroutine($this->generator->current()); $this->nextCoroutine($this->generator->current());
} catch (\Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
$this->result->reject($throwable); $this->result->reject($throwable);
} }
@ -78,10 +77,8 @@ final class Coroutine implements PromiseInterface
/** /**
* Create a new coroutine. * Create a new coroutine.
*
* @return self
*/ */
public static function of(callable $generatorFn) public static function of(callable $generatorFn): self
{ {
return new self($generatorFn); return new self($generatorFn);
} }
@ -89,42 +86,42 @@ final class Coroutine implements PromiseInterface
public function then( public function then(
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
return $this->result->then($onFulfilled, $onRejected); return $this->result->then($onFulfilled, $onRejected);
} }
public function otherwise(callable $onRejected) public function otherwise(callable $onRejected): PromiseInterface
{ {
return $this->result->otherwise($onRejected); return $this->result->otherwise($onRejected);
} }
public function wait($unwrap = true) public function wait(bool $unwrap = true)
{ {
return $this->result->wait($unwrap); return $this->result->wait($unwrap);
} }
public function getState() public function getState(): string
{ {
return $this->result->getState(); return $this->result->getState();
} }
public function resolve($value) public function resolve($value): void
{ {
$this->result->resolve($value); $this->result->resolve($value);
} }
public function reject($reason) public function reject($reason): void
{ {
$this->result->reject($reason); $this->result->reject($reason);
} }
public function cancel() public function cancel(): void
{ {
$this->currentPromise->cancel(); $this->currentPromise->cancel();
$this->result->cancel(); $this->result->cancel();
} }
private function nextCoroutine($yielded) private function nextCoroutine($yielded): void
{ {
$this->currentPromise = Create::promiseFor($yielded) $this->currentPromise = Create::promiseFor($yielded)
->then([$this, '_handleSuccess'], [$this, '_handleFailure']); ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
@ -133,7 +130,7 @@ final class Coroutine implements PromiseInterface
/** /**
* @internal * @internal
*/ */
public function _handleSuccess($value) public function _handleSuccess($value): void
{ {
unset($this->currentPromise); unset($this->currentPromise);
try { try {
@ -143,8 +140,6 @@ final class Coroutine implements PromiseInterface
} else { } else {
$this->result->resolve($value); $this->result->resolve($value);
} }
} catch (Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
$this->result->reject($throwable); $this->result->reject($throwable);
} }
@ -153,15 +148,13 @@ final class Coroutine implements PromiseInterface
/** /**
* @internal * @internal
*/ */
public function _handleFailure($reason) public function _handleFailure($reason): void
{ {
unset($this->currentPromise); unset($this->currentPromise);
try { try {
$nextYield = $this->generator->throw(Create::exceptionFor($reason)); $nextYield = $this->generator->throw(Create::exceptionFor($reason));
// The throw was caught, so keep iterating on the coroutine // The throw was caught, so keep iterating on the coroutine
$this->nextCoroutine($nextYield); $this->nextCoroutine($nextYield);
} catch (Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
$this->result->reject($throwable); $this->result->reject($throwable);
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
final class Create final class Create
@ -8,10 +10,8 @@ final class Create
* Creates a promise for a value if the value is not a promise. * Creates a promise for a value if the value is not a promise.
* *
* @param mixed $value Promise or value. * @param mixed $value Promise or value.
*
* @return PromiseInterface
*/ */
public static function promiseFor($value) public static function promiseFor($value): PromiseInterface
{ {
if ($value instanceof PromiseInterface) { if ($value instanceof PromiseInterface) {
return $value; return $value;
@ -23,6 +23,7 @@ final class Create
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null; $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn); $promise = new Promise($wfn, $cfn);
$value->then([$promise, 'resolve'], [$promise, 'reject']); $value->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise; return $promise;
} }
@ -34,10 +35,8 @@ final class Create
* If the provided reason is a promise, then it is returned as-is. * If the provided reason is a promise, then it is returned as-is.
* *
* @param mixed $reason Promise or reason. * @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*/ */
public static function rejectionFor($reason) public static function rejectionFor($reason): PromiseInterface
{ {
if ($reason instanceof PromiseInterface) { if ($reason instanceof PromiseInterface) {
return $reason; return $reason;
@ -50,12 +49,10 @@ final class Create
* Create an exception for a rejected promise value. * Create an exception for a rejected promise value.
* *
* @param mixed $reason * @param mixed $reason
*
* @return \Exception|\Throwable
*/ */
public static function exceptionFor($reason) public static function exceptionFor($reason): \Throwable
{ {
if ($reason instanceof \Exception || $reason instanceof \Throwable) { if ($reason instanceof \Throwable) {
return $reason; return $reason;
} }
@ -66,10 +63,8 @@ final class Create
* Returns an iterator for the given value. * Returns an iterator for the given value.
* *
* @param mixed $value * @param mixed $value
*
* @return \Iterator
*/ */
public static function iterFor($value) public static function iterFor($value): \Iterator
{ {
if ($value instanceof \Iterator) { if ($value instanceof \Iterator) {
return $value; return $value;

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
final class Each final class Each
@ -18,19 +20,15 @@ final class Each
* side effects and choose to resolve or reject the aggregate if needed. * side effects and choose to resolve or reject the aggregate if needed.
* *
* @param mixed $iterable Iterator or array to iterate over. * @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/ */
public static function of( public static function of(
$iterable, $iterable,
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
return (new EachPromise($iterable, [ return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled, 'fulfilled' => $onFulfilled,
'rejected' => $onRejected 'rejected' => $onRejected,
]))->promise(); ]))->promise();
} }
@ -44,21 +42,17 @@ final class Each
* *
* @param mixed $iterable * @param mixed $iterable
* @param int|callable $concurrency * @param int|callable $concurrency
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/ */
public static function ofLimit( public static function ofLimit(
$iterable, $iterable,
$concurrency, $concurrency,
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
return (new EachPromise($iterable, [ return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled, 'fulfilled' => $onFulfilled,
'rejected' => $onRejected, 'rejected' => $onRejected,
'concurrency' => $concurrency 'concurrency' => $concurrency,
]))->promise(); ]))->promise();
} }
@ -69,20 +63,17 @@ final class Each
* *
* @param mixed $iterable * @param mixed $iterable
* @param int|callable $concurrency * @param int|callable $concurrency
* @param callable $onFulfilled
*
* @return PromiseInterface
*/ */
public static function ofLimitAll( public static function ofLimitAll(
$iterable, $iterable,
$concurrency, $concurrency,
callable $onFulfilled = null callable $onFulfilled = null
) { ): PromiseInterface {
return each_limit( return self::ofLimit(
$iterable, $iterable,
$concurrency, $concurrency,
$onFulfilled, $onFulfilled,
function ($reason, $idx, PromiseInterface $aggregate) { function ($reason, $idx, PromiseInterface $aggregate): void {
$aggregate->reject($reason); $aggregate->reject($reason);
} }
); );

View File

@ -1,10 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
* Represents a promise that iterates over many promises and invokes * Represents a promise that iterates over many promises and invokes
* side-effect functions in the process. * side-effect functions in the process.
*
* @final
*/ */
class EachPromise implements PromisorInterface class EachPromise implements PromisorInterface
{ {
@ -69,7 +73,7 @@ class EachPromise implements PromisorInterface
} }
/** @psalm-suppress InvalidNullableReturnType */ /** @psalm-suppress InvalidNullableReturnType */
public function promise() public function promise(): PromiseInterface
{ {
if ($this->aggregate) { if ($this->aggregate) {
return $this->aggregate; return $this->aggregate;
@ -82,21 +86,18 @@ class EachPromise implements PromisorInterface
$this->refillPending(); $this->refillPending();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->aggregate->reject($e); $this->aggregate->reject($e);
} catch (\Exception $e) {
$this->aggregate->reject($e);
} }
/** /**
* @psalm-suppress NullableReturnStatement * @psalm-suppress NullableReturnStatement
* @phpstan-ignore-next-line
*/ */
return $this->aggregate; return $this->aggregate;
} }
private function createPromise() private function createPromise(): void
{ {
$this->mutex = false; $this->mutex = false;
$this->aggregate = new Promise(function () { $this->aggregate = new Promise(function (): void {
if ($this->checkIfFinished()) { if ($this->checkIfFinished()) {
return; return;
} }
@ -113,7 +114,7 @@ class EachPromise implements PromisorInterface
}); });
// Clear the references when the promise is resolved. // Clear the references when the promise is resolved.
$clearFn = function () { $clearFn = function (): void {
$this->iterable = $this->concurrency = $this->pending = null; $this->iterable = $this->concurrency = $this->pending = null;
$this->onFulfilled = $this->onRejected = null; $this->onFulfilled = $this->onRejected = null;
$this->nextPendingIndex = 0; $this->nextPendingIndex = 0;
@ -122,17 +123,19 @@ class EachPromise implements PromisorInterface
$this->aggregate->then($clearFn, $clearFn); $this->aggregate->then($clearFn, $clearFn);
} }
private function refillPending() private function refillPending(): void
{ {
if (!$this->concurrency) { if (!$this->concurrency) {
// Add all pending promises. // Add all pending promises.
while ($this->addPending() && $this->advanceIterator()); while ($this->addPending() && $this->advanceIterator()) {
}
return; return;
} }
// Add only up to N pending promises. // Add only up to N pending promises.
$concurrency = is_callable($this->concurrency) $concurrency = is_callable($this->concurrency)
? call_user_func($this->concurrency, count($this->pending)) ? ($this->concurrency)(count($this->pending))
: $this->concurrency; : $this->concurrency;
$concurrency = max($concurrency - count($this->pending), 0); $concurrency = max($concurrency - count($this->pending), 0);
// Concurrency may be set to 0 to disallow new promises. // Concurrency may be set to 0 to disallow new promises.
@ -147,10 +150,11 @@ class EachPromise implements PromisorInterface
// next value to yield until promise callbacks are called. // next value to yield until promise callbacks are called.
while (--$concurrency while (--$concurrency
&& $this->advanceIterator() && $this->advanceIterator()
&& $this->addPending()); && $this->addPending()) {
}
} }
private function addPending() private function addPending(): bool
{ {
if (!$this->iterable || !$this->iterable->valid()) { if (!$this->iterable || !$this->iterable->valid()) {
return false; return false;
@ -164,10 +168,9 @@ class EachPromise implements PromisorInterface
$idx = $this->nextPendingIndex++; $idx = $this->nextPendingIndex++;
$this->pending[$idx] = $promise->then( $this->pending[$idx] = $promise->then(
function ($value) use ($idx, $key) { function ($value) use ($idx, $key): void {
if ($this->onFulfilled) { if ($this->onFulfilled) {
call_user_func( ($this->onFulfilled)(
$this->onFulfilled,
$value, $value,
$key, $key,
$this->aggregate $this->aggregate
@ -175,10 +178,9 @@ class EachPromise implements PromisorInterface
} }
$this->step($idx); $this->step($idx);
}, },
function ($reason) use ($idx, $key) { function ($reason) use ($idx, $key): void {
if ($this->onRejected) { if ($this->onRejected) {
call_user_func( ($this->onRejected)(
$this->onRejected,
$reason, $reason,
$key, $key,
$this->aggregate $this->aggregate
@ -191,7 +193,7 @@ class EachPromise implements PromisorInterface
return true; return true;
} }
private function advanceIterator() private function advanceIterator(): bool
{ {
// Place a lock on the iterator so that we ensure to not recurse, // Place a lock on the iterator so that we ensure to not recurse,
// preventing fatal generator errors. // preventing fatal generator errors.
@ -204,19 +206,17 @@ class EachPromise implements PromisorInterface
try { try {
$this->iterable->next(); $this->iterable->next();
$this->mutex = false; $this->mutex = false;
return true; return true;
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->aggregate->reject($e); $this->aggregate->reject($e);
$this->mutex = false; $this->mutex = false;
return false;
} catch (\Exception $e) {
$this->aggregate->reject($e);
$this->mutex = false;
return false; return false;
} }
} }
private function step($idx) private function step(int $idx): void
{ {
// If the promise was already resolved, then ignore this step. // If the promise was already resolved, then ignore this step.
if (Is::settled($this->aggregate)) { if (Is::settled($this->aggregate)) {
@ -234,11 +234,12 @@ class EachPromise implements PromisorInterface
} }
} }
private function checkIfFinished() private function checkIfFinished(): bool
{ {
if (!$this->pending && !$this->iterable->valid()) { if (!$this->pending && !$this->iterable->valid()) {
// Resolve the promise if there's nothing left to do. // Resolve the promise if there's nothing left to do.
$this->aggregate->resolve(null); $this->aggregate->resolve(null);
return true; return true;
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
* *
* Thenning off of this promise will invoke the onFulfilled callback * Thenning off of this promise will invoke the onFulfilled callback
* immediately and ignore other callbacks. * immediately and ignore other callbacks.
*
* @final
*/ */
class FulfilledPromise implements PromiseInterface class FulfilledPromise implements PromiseInterface
{ {
private $value; private $value;
/**
* @param mixed $value
*/
public function __construct($value) public function __construct($value)
{ {
if (is_object($value) && method_exists($value, 'then')) { if (is_object($value) && method_exists($value, 'then')) {
@ -26,7 +33,7 @@ class FulfilledPromise implements PromiseInterface
public function then( public function then(
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
// Return itself if there is no onFulfilled function. // Return itself if there is no onFulfilled function.
if (!$onFulfilled) { if (!$onFulfilled) {
return $this; return $this;
@ -35,14 +42,12 @@ class FulfilledPromise implements PromiseInterface
$queue = Utils::queue(); $queue = Utils::queue();
$p = new Promise([$queue, 'run']); $p = new Promise([$queue, 'run']);
$value = $this->value; $value = $this->value;
$queue->add(static function () use ($p, $value, $onFulfilled) { $queue->add(static function () use ($p, $value, $onFulfilled): void {
if (Is::pending($p)) { if (Is::pending($p)) {
try { try {
$p->resolve($onFulfilled($value)); $p->resolve($onFulfilled($value));
} catch (\Throwable $e) { } catch (\Throwable $e) {
$p->reject($e); $p->reject($e);
} catch (\Exception $e) {
$p->reject($e);
} }
} }
}); });
@ -50,34 +55,34 @@ class FulfilledPromise implements PromiseInterface
return $p; return $p;
} }
public function otherwise(callable $onRejected) public function otherwise(callable $onRejected): PromiseInterface
{ {
return $this->then(null, $onRejected); return $this->then(null, $onRejected);
} }
public function wait($unwrap = true, $defaultDelivery = null) public function wait(bool $unwrap = true)
{ {
return $unwrap ? $this->value : null; return $unwrap ? $this->value : null;
} }
public function getState() public function getState(): string
{ {
return self::FULFILLED; return self::FULFILLED;
} }
public function resolve($value) public function resolve($value): void
{ {
if ($value !== $this->value) { if ($value !== $this->value) {
throw new \LogicException("Cannot resolve a fulfilled promise"); throw new \LogicException('Cannot resolve a fulfilled promise');
} }
} }
public function reject($reason) public function reject($reason): void
{ {
throw new \LogicException("Cannot reject a fulfilled promise"); throw new \LogicException('Cannot reject a fulfilled promise');
} }
public function cancel() public function cancel(): void
{ {
// pass // pass
} }

View File

@ -1,45 +1,39 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
final class Is final class Is
{ {
/** /**
* Returns true if a promise is pending. * Returns true if a promise is pending.
*
* @return bool
*/ */
public static function pending(PromiseInterface $promise) public static function pending(PromiseInterface $promise): bool
{ {
return $promise->getState() === PromiseInterface::PENDING; return $promise->getState() === PromiseInterface::PENDING;
} }
/** /**
* Returns true if a promise is fulfilled or rejected. * Returns true if a promise is fulfilled or rejected.
*
* @return bool
*/ */
public static function settled(PromiseInterface $promise) public static function settled(PromiseInterface $promise): bool
{ {
return $promise->getState() !== PromiseInterface::PENDING; return $promise->getState() !== PromiseInterface::PENDING;
} }
/** /**
* Returns true if a promise is fulfilled. * Returns true if a promise is fulfilled.
*
* @return bool
*/ */
public static function fulfilled(PromiseInterface $promise) public static function fulfilled(PromiseInterface $promise): bool
{ {
return $promise->getState() === PromiseInterface::FULFILLED; return $promise->getState() === PromiseInterface::FULFILLED;
} }
/** /**
* Returns true if a promise is rejected. * Returns true if a promise is rejected.
*
* @return bool
*/ */
public static function rejected(PromiseInterface $promise) public static function rejected(PromiseInterface $promise): bool
{ {
return $promise->getState() === PromiseInterface::REJECTED; return $promise->getState() === PromiseInterface::REJECTED;
} }

View File

@ -1,11 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
* Promises/A+ implementation that avoids recursion when possible. * Promises/A+ implementation that avoids recursion when possible.
* *
* @link https://promisesaplus.com/ * @see https://promisesaplus.com/
*
* @final
*/ */
class Promise implements PromiseInterface class Promise implements PromiseInterface
{ {
@ -31,33 +35,36 @@ class Promise implements PromiseInterface
public function then( public function then(
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
if ($this->state === self::PENDING) { if ($this->state === self::PENDING) {
$p = new Promise(null, [$this, 'cancel']); $p = new Promise(null, [$this, 'cancel']);
$this->handlers[] = [$p, $onFulfilled, $onRejected]; $this->handlers[] = [$p, $onFulfilled, $onRejected];
$p->waitList = $this->waitList; $p->waitList = $this->waitList;
$p->waitList[] = $this; $p->waitList[] = $this;
return $p; return $p;
} }
// Return a fulfilled promise and immediately invoke any callbacks. // Return a fulfilled promise and immediately invoke any callbacks.
if ($this->state === self::FULFILLED) { if ($this->state === self::FULFILLED) {
$promise = Create::promiseFor($this->result); $promise = Create::promiseFor($this->result);
return $onFulfilled ? $promise->then($onFulfilled) : $promise; return $onFulfilled ? $promise->then($onFulfilled) : $promise;
} }
// It's either cancelled or rejected, so return a rejected promise // It's either cancelled or rejected, so return a rejected promise
// and immediately invoke any callbacks. // and immediately invoke any callbacks.
$rejection = Create::rejectionFor($this->result); $rejection = Create::rejectionFor($this->result);
return $onRejected ? $rejection->then(null, $onRejected) : $rejection; return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
} }
public function otherwise(callable $onRejected) public function otherwise(callable $onRejected): PromiseInterface
{ {
return $this->then(null, $onRejected); return $this->then(null, $onRejected);
} }
public function wait($unwrap = true) public function wait(bool $unwrap = true)
{ {
$this->waitIfPending(); $this->waitIfPending();
@ -73,12 +80,12 @@ class Promise implements PromiseInterface
} }
} }
public function getState() public function getState(): string
{ {
return $this->state; return $this->state;
} }
public function cancel() public function cancel(): void
{ {
if ($this->state !== self::PENDING) { if ($this->state !== self::PENDING) {
return; return;
@ -93,8 +100,6 @@ class Promise implements PromiseInterface
$fn(); $fn();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->reject($e); $this->reject($e);
} catch (\Exception $e) {
$this->reject($e);
} }
} }
@ -105,17 +110,17 @@ class Promise implements PromiseInterface
} }
} }
public function resolve($value) public function resolve($value): void
{ {
$this->settle(self::FULFILLED, $value); $this->settle(self::FULFILLED, $value);
} }
public function reject($reason) public function reject($reason): void
{ {
$this->settle(self::REJECTED, $reason); $this->settle(self::REJECTED, $reason);
} }
private function settle($state, $value) private function settle(string $state, $value): void
{ {
if ($this->state !== self::PENDING) { if ($this->state !== self::PENDING) {
// Ignore calls with the same resolution. // Ignore calls with the same resolution.
@ -148,7 +153,7 @@ class Promise implements PromiseInterface
if (!is_object($value) || !method_exists($value, 'then')) { if (!is_object($value) || !method_exists($value, 'then')) {
$id = $state === self::FULFILLED ? 1 : 2; $id = $state === self::FULFILLED ? 1 : 2;
// It's a success, so resolve the handlers in the queue. // It's a success, so resolve the handlers in the queue.
Utils::queue()->add(static function () use ($id, $value, $handlers) { Utils::queue()->add(static function () use ($id, $value, $handlers): void {
foreach ($handlers as $handler) { foreach ($handlers as $handler) {
self::callHandler($id, $value, $handler); self::callHandler($id, $value, $handler);
} }
@ -159,12 +164,12 @@ class Promise implements PromiseInterface
} else { } else {
// Resolve the handlers when the forwarded promise is resolved. // Resolve the handlers when the forwarded promise is resolved.
$value->then( $value->then(
static function ($value) use ($handlers) { static function ($value) use ($handlers): void {
foreach ($handlers as $handler) { foreach ($handlers as $handler) {
self::callHandler(1, $value, $handler); self::callHandler(1, $value, $handler);
} }
}, },
static function ($reason) use ($handlers) { static function ($reason) use ($handlers): void {
foreach ($handlers as $handler) { foreach ($handlers as $handler) {
self::callHandler(2, $reason, $handler); self::callHandler(2, $reason, $handler);
} }
@ -180,7 +185,7 @@ class Promise implements PromiseInterface
* @param mixed $value Value to pass to the callback. * @param mixed $value Value to pass to the callback.
* @param array $handler Array of handler data (promise and callbacks). * @param array $handler Array of handler data (promise and callbacks).
*/ */
private static function callHandler($index, $value, array $handler) private static function callHandler(int $index, $value, array $handler): void
{ {
/** @var PromiseInterface $promise */ /** @var PromiseInterface $promise */
$promise = $handler[0]; $promise = $handler[0];
@ -211,12 +216,10 @@ class Promise implements PromiseInterface
} }
} catch (\Throwable $reason) { } catch (\Throwable $reason) {
$promise->reject($reason); $promise->reject($reason);
} catch (\Exception $reason) {
$promise->reject($reason);
} }
} }
private function waitIfPending() private function waitIfPending(): void
{ {
if ($this->state !== self::PENDING) { if ($this->state !== self::PENDING) {
return; return;
@ -240,13 +243,13 @@ class Promise implements PromiseInterface
} }
} }
private function invokeWaitFn() private function invokeWaitFn(): void
{ {
try { try {
$wfn = $this->waitFn; $wfn = $this->waitFn;
$this->waitFn = null; $this->waitFn = null;
$wfn(true); $wfn(true);
} catch (\Exception $reason) { } catch (\Throwable $reason) {
if ($this->state === self::PENDING) { if ($this->state === self::PENDING) {
// The promise has not been resolved yet, so reject the promise // The promise has not been resolved yet, so reject the promise
// with the exception. // with the exception.
@ -259,7 +262,7 @@ class Promise implements PromiseInterface
} }
} }
private function invokeWaitList() private function invokeWaitList(): void
{ {
$waitList = $this->waitList; $waitList = $this->waitList;
$this->waitList = null; $this->waitList = null;

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -9,13 +11,13 @@ namespace GuzzleHttp\Promise;
* which registers callbacks to receive either a promises eventual value or * which registers callbacks to receive either a promises eventual value or
* the reason why the promise cannot be fulfilled. * the reason why the promise cannot be fulfilled.
* *
* @link https://promisesaplus.com/ * @see https://promisesaplus.com/
*/ */
interface PromiseInterface interface PromiseInterface
{ {
const PENDING = 'pending'; public const PENDING = 'pending';
const FULFILLED = 'fulfilled'; public const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'; public const REJECTED = 'rejected';
/** /**
* Appends fulfillment and rejection handlers to the promise, and returns * Appends fulfillment and rejection handlers to the promise, and returns
@ -23,13 +25,11 @@ interface PromiseInterface
* *
* @param callable $onFulfilled Invoked when the promise fulfills. * @param callable $onFulfilled Invoked when the promise fulfills.
* @param callable $onRejected Invoked when the promise is rejected. * @param callable $onRejected Invoked when the promise is rejected.
*
* @return PromiseInterface
*/ */
public function then( public function then(
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
); ): PromiseInterface;
/** /**
* Appends a rejection handler callback to the promise, and returns a new * Appends a rejection handler callback to the promise, and returns a new
@ -38,20 +38,16 @@ interface PromiseInterface
* fulfilled. * fulfilled.
* *
* @param callable $onRejected Invoked when the promise is rejected. * @param callable $onRejected Invoked when the promise is rejected.
*
* @return PromiseInterface
*/ */
public function otherwise(callable $onRejected); public function otherwise(callable $onRejected): PromiseInterface;
/** /**
* Get the state of the promise ("pending", "rejected", or "fulfilled"). * Get the state of the promise ("pending", "rejected", or "fulfilled").
* *
* The three states can be checked against the constants defined on * The three states can be checked against the constants defined on
* PromiseInterface: PENDING, FULFILLED, and REJECTED. * PromiseInterface: PENDING, FULFILLED, and REJECTED.
*
* @return string
*/ */
public function getState(); public function getState(): string;
/** /**
* Resolve the promise with the given value. * Resolve the promise with the given value.
@ -60,7 +56,7 @@ interface PromiseInterface
* *
* @throws \RuntimeException if the promise is already resolved. * @throws \RuntimeException if the promise is already resolved.
*/ */
public function resolve($value); public function resolve($value): void;
/** /**
* Reject the promise with the given reason. * Reject the promise with the given reason.
@ -69,14 +65,14 @@ interface PromiseInterface
* *
* @throws \RuntimeException if the promise is already resolved. * @throws \RuntimeException if the promise is already resolved.
*/ */
public function reject($reason); public function reject($reason): void;
/** /**
* Cancels the promise if possible. * Cancels the promise if possible.
* *
* @link https://github.com/promises-aplus/cancellation-spec/issues/7 * @see https://github.com/promises-aplus/cancellation-spec/issues/7
*/ */
public function cancel(); public function cancel(): void;
/** /**
* Waits until the promise completes if possible. * Waits until the promise completes if possible.
@ -86,12 +82,10 @@ interface PromiseInterface
* *
* If the promise cannot be waited on, then the promise will be rejected. * If the promise cannot be waited on, then the promise will be rejected.
* *
* @param bool $unwrap
*
* @return mixed * @return mixed
* *
* @throws \LogicException if the promise has no wait function or if the * @throws \LogicException if the promise has no wait function or if the
* promise does not settle after waiting. * promise does not settle after waiting.
*/ */
public function wait($unwrap = true); public function wait(bool $unwrap = true);
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -9,8 +11,6 @@ interface PromisorInterface
{ {
/** /**
* Returns a promise. * Returns a promise.
*
* @return PromiseInterface
*/ */
public function promise(); public function promise(): PromiseInterface;
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
* *
* Thenning off of this promise will invoke the onRejected callback * Thenning off of this promise will invoke the onRejected callback
* immediately and ignore other callbacks. * immediately and ignore other callbacks.
*
* @final
*/ */
class RejectedPromise implements PromiseInterface class RejectedPromise implements PromiseInterface
{ {
private $reason; private $reason;
/**
* @param mixed $reason
*/
public function __construct($reason) public function __construct($reason)
{ {
if (is_object($reason) && method_exists($reason, 'then')) { if (is_object($reason) && method_exists($reason, 'then')) {
@ -26,7 +33,7 @@ class RejectedPromise implements PromiseInterface
public function then( public function then(
callable $onFulfilled = null, callable $onFulfilled = null,
callable $onRejected = null callable $onRejected = null
) { ): PromiseInterface {
// If there's no onRejected callback then just return self. // If there's no onRejected callback then just return self.
if (!$onRejected) { if (!$onRejected) {
return $this; return $this;
@ -35,7 +42,7 @@ class RejectedPromise implements PromiseInterface
$queue = Utils::queue(); $queue = Utils::queue();
$reason = $this->reason; $reason = $this->reason;
$p = new Promise([$queue, 'run']); $p = new Promise([$queue, 'run']);
$queue->add(static function () use ($p, $reason, $onRejected) { $queue->add(static function () use ($p, $reason, $onRejected): void {
if (Is::pending($p)) { if (Is::pending($p)) {
try { try {
// Return a resolved promise if onRejected does not throw. // Return a resolved promise if onRejected does not throw.
@ -43,9 +50,6 @@ class RejectedPromise implements PromiseInterface
} catch (\Throwable $e) { } catch (\Throwable $e) {
// onRejected threw, so return a rejected promise. // onRejected threw, so return a rejected promise.
$p->reject($e); $p->reject($e);
} catch (\Exception $e) {
// onRejected threw, so return a rejected promise.
$p->reject($e);
} }
} }
}); });
@ -53,12 +57,12 @@ class RejectedPromise implements PromiseInterface
return $p; return $p;
} }
public function otherwise(callable $onRejected) public function otherwise(callable $onRejected): PromiseInterface
{ {
return $this->then(null, $onRejected); return $this->then(null, $onRejected);
} }
public function wait($unwrap = true, $defaultDelivery = null) public function wait(bool $unwrap = true)
{ {
if ($unwrap) { if ($unwrap) {
throw Create::exceptionFor($this->reason); throw Create::exceptionFor($this->reason);
@ -67,24 +71,24 @@ class RejectedPromise implements PromiseInterface
return null; return null;
} }
public function getState() public function getState(): string
{ {
return self::REJECTED; return self::REJECTED;
} }
public function resolve($value) public function resolve($value): void
{ {
throw new \LogicException("Cannot resolve a rejected promise"); throw new \LogicException('Cannot resolve a rejected promise');
} }
public function reject($reason) public function reject($reason): void
{ {
if ($reason !== $this->reason) { if ($reason !== $this->reason) {
throw new \LogicException("Cannot reject a rejected promise"); throw new \LogicException('Cannot reject a rejected promise');
} }
} }
public function cancel() public function cancel(): void
{ {
// pass // pass
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -14,9 +16,9 @@ class RejectionException extends \RuntimeException
/** /**
* @param mixed $reason Rejection reason. * @param mixed $reason Rejection reason.
* @param string $description Optional description * @param string|null $description Optional description.
*/ */
public function __construct($reason, $description = null) public function __construct($reason, string $description = null)
{ {
$this->reason = $reason; $this->reason = $reason;
@ -29,8 +31,7 @@ class RejectionException extends \RuntimeException
) { ) {
$message .= ' with reason: '.$this->reason; $message .= ' with reason: '.$this->reason;
} elseif ($reason instanceof \JsonSerializable) { } elseif ($reason instanceof \JsonSerializable) {
$message .= ' with reason: ' $message .= ' with reason: '.json_encode($this->reason, JSON_PRETTY_PRINT);
. json_encode($this->reason, JSON_PRETTY_PRINT);
} }
parent::__construct($message); parent::__construct($message);

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
/** /**
@ -10,16 +12,18 @@ namespace GuzzleHttp\Promise;
* by calling the `run()` function of the global task queue in an event loop. * by calling the `run()` function of the global task queue in an event loop.
* *
* GuzzleHttp\Promise\Utils::queue()->run(); * GuzzleHttp\Promise\Utils::queue()->run();
*
* @final
*/ */
class TaskQueue implements TaskQueueInterface class TaskQueue implements TaskQueueInterface
{ {
private $enableShutdown = true; private $enableShutdown = true;
private $queue = []; private $queue = [];
public function __construct($withShutdown = true) public function __construct(bool $withShutdown = true)
{ {
if ($withShutdown) { if ($withShutdown) {
register_shutdown_function(function () { register_shutdown_function(function (): void {
if ($this->enableShutdown) { if ($this->enableShutdown) {
// Only run the tasks if an E_ERROR didn't occur. // Only run the tasks if an E_ERROR didn't occur.
$err = error_get_last(); $err = error_get_last();
@ -31,17 +35,17 @@ class TaskQueue implements TaskQueueInterface
} }
} }
public function isEmpty() public function isEmpty(): bool
{ {
return !$this->queue; return !$this->queue;
} }
public function add(callable $task) public function add(callable $task): void
{ {
$this->queue[] = $task; $this->queue[] = $task;
} }
public function run() public function run(): void
{ {
while ($task = array_shift($this->queue)) { while ($task = array_shift($this->queue)) {
/** @var callable $task */ /** @var callable $task */
@ -60,7 +64,7 @@ class TaskQueue implements TaskQueueInterface
* *
* Note: This shutdown will occur before any destructors are triggered. * Note: This shutdown will occur before any destructors are triggered.
*/ */
public function disableShutdown() public function disableShutdown(): void
{ {
$this->enableShutdown = false; $this->enableShutdown = false;
} }

View File

@ -1,24 +1,24 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
interface TaskQueueInterface interface TaskQueueInterface
{ {
/** /**
* Returns true if the queue is empty. * Returns true if the queue is empty.
*
* @return bool
*/ */
public function isEmpty(); public function isEmpty(): bool;
/** /**
* Adds a task to the queue that will be executed the next time run is * Adds a task to the queue that will be executed the next time run is
* called. * called.
*/ */
public function add(callable $task); public function add(callable $task): void;
/** /**
* Execute all of the pending task in the queue. * Execute all of the pending task in the queue.
*/ */
public function run(); public function run(): void;
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GuzzleHttp\Promise; namespace GuzzleHttp\Promise;
final class Utils final class Utils
@ -17,11 +19,9 @@ final class Utils
* } * }
* </code> * </code>
* *
* @param TaskQueueInterface $assign Optionally specify a new queue instance. * @param TaskQueueInterface|null $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*/ */
public static function queue(TaskQueueInterface $assign = null) public static function queue(TaskQueueInterface $assign = null): TaskQueueInterface
{ {
static $queue; static $queue;
@ -39,22 +39,18 @@ final class Utils
* returns a promise that is fulfilled or rejected with the result. * returns a promise that is fulfilled or rejected with the result.
* *
* @param callable $task Task function to run. * @param callable $task Task function to run.
*
* @return PromiseInterface
*/ */
public static function task(callable $task) public static function task(callable $task): PromiseInterface
{ {
$queue = self::queue(); $queue = self::queue();
$promise = new Promise([$queue, 'run']); $promise = new Promise([$queue, 'run']);
$queue->add(function () use ($task, $promise) { $queue->add(function () use ($task, $promise): void {
try { try {
if (Is::pending($promise)) { if (Is::pending($promise)) {
$promise->resolve($task()); $promise->resolve($task());
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
$promise->reject($e); $promise->reject($e);
} catch (\Exception $e) {
$promise->reject($e);
} }
}); });
@ -72,22 +68,18 @@ final class Utils
* key mapping to the rejection reason of the promise. * key mapping to the rejection reason of the promise.
* *
* @param PromiseInterface $promise Promise or value. * @param PromiseInterface $promise Promise or value.
*
* @return array
*/ */
public static function inspect(PromiseInterface $promise) public static function inspect(PromiseInterface $promise): array
{ {
try { try {
return [ return [
'state' => PromiseInterface::FULFILLED, 'state' => PromiseInterface::FULFILLED,
'value' => $promise->wait() 'value' => $promise->wait(),
]; ];
} catch (RejectionException $e) { } catch (RejectionException $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Throwable $e) { } catch (\Throwable $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} catch (\Exception $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} }
} }
@ -100,14 +92,12 @@ final class Utils
* @see inspect for the inspection state array format. * @see inspect for the inspection state array format.
* *
* @param PromiseInterface[] $promises Traversable of promises to wait upon. * @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
*/ */
public static function inspectAll($promises) public static function inspectAll($promises): array
{ {
$results = []; $results = [];
foreach ($promises as $key => $promise) { foreach ($promises as $key => $promise) {
$results[$key] = inspect($promise); $results[$key] = self::inspect($promise);
} }
return $results; return $results;
@ -122,12 +112,9 @@ final class Utils
* *
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on. * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
* *
* @return array * @throws \Throwable on error
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*/ */
public static function unwrap($promises) public static function unwrap($promises): array
{ {
$results = []; $results = [];
foreach ($promises as $key => $promise) { foreach ($promises as $key => $promise) {
@ -147,22 +134,21 @@ final class Utils
* *
* @param mixed $promises Promises or values. * @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution. * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*/ */
public static function all($promises, $recursive = false) public static function all($promises, bool $recursive = false): PromiseInterface
{ {
$results = []; $results = [];
$promise = Each::of( $promise = Each::of(
$promises, $promises,
function ($value, $idx) use (&$results) { function ($value, $idx) use (&$results): void {
$results[$idx] = $value; $results[$idx] = $value;
}, },
function ($reason, $idx, Promise $aggregate) { function ($reason, $idx, Promise $aggregate): void {
$aggregate->reject($reason); $aggregate->reject($reason);
} }
)->then(function () use (&$results) { )->then(function () use (&$results) {
ksort($results); ksort($results);
return $results; return $results;
}); });
@ -173,6 +159,7 @@ final class Utils
return self::all($promises, $recursive); return self::all($promises, $recursive);
} }
} }
return $results; return $results;
}); });
} }
@ -193,17 +180,15 @@ final class Utils
* *
* @param int $count Total number of promises. * @param int $count Total number of promises.
* @param mixed $promises Promises or values. * @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/ */
public static function some($count, $promises) public static function some(int $count, $promises): PromiseInterface
{ {
$results = []; $results = [];
$rejections = []; $rejections = [];
return Each::of( return Each::of(
$promises, $promises,
function ($value, $idx, PromiseInterface $p) use (&$results, $count) { function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
if (Is::settled($p)) { if (Is::settled($p)) {
return; return;
} }
@ -212,7 +197,7 @@ final class Utils
$p->resolve(null); $p->resolve(null);
} }
}, },
function ($reason) use (&$rejections) { function ($reason) use (&$rejections): void {
$rejections[] = $reason; $rejections[] = $reason;
} }
)->then( )->then(
@ -224,6 +209,7 @@ final class Utils
); );
} }
ksort($results); ksort($results);
return array_values($results); return array_values($results);
} }
); );
@ -234,10 +220,8 @@ final class Utils
* fulfillment value is not an array of 1 but the value directly. * fulfillment value is not an array of 1 but the value directly.
* *
* @param mixed $promises Promises or values. * @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/ */
public static function any($promises) public static function any($promises): PromiseInterface
{ {
return self::some(1, $promises)->then(function ($values) { return self::some(1, $promises)->then(function ($values) {
return $values[0]; return $values[0];
@ -253,23 +237,22 @@ final class Utils
* @see inspect for the inspection state array format. * @see inspect for the inspection state array format.
* *
* @param mixed $promises Promises or values. * @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/ */
public static function settle($promises) public static function settle($promises): PromiseInterface
{ {
$results = []; $results = [];
return Each::of( return Each::of(
$promises, $promises,
function ($value, $idx) use (&$results) { function ($value, $idx) use (&$results): void {
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
}, },
function ($reason, $idx) use (&$results) { function ($reason, $idx) use (&$results): void {
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
} }
)->then(function () use (&$results) { )->then(function () use (&$results) {
ksort($results); ksort($results);
return $results; return $results;
}); });
} }

View File

@ -1,363 +0,0 @@
<?php
namespace GuzzleHttp\Promise;
/**
* Get the global task queue used for promise resolution.
*
* This task queue MUST be run in an event loop in order for promises to be
* settled asynchronously. It will be automatically run when synchronously
* waiting on a promise.
*
* <code>
* while ($eventLoop->isRunning()) {
* GuzzleHttp\Promise\queue()->run();
* }
* </code>
*
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*
* @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
*/
function queue(TaskQueueInterface $assign = null)
{
return Utils::queue($assign);
}
/**
* Adds a function to run in the task queue when it is next `run()` and returns
* a promise that is fulfilled or rejected with the result.
*
* @param callable $task Task function to run.
*
* @return PromiseInterface
*
* @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
*/
function task(callable $task)
{
return Utils::task($task);
}
/**
* Creates a promise for a value if the value is not a promise.
*
* @param mixed $value Promise or value.
*
* @return PromiseInterface
*
* @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
*/
function promise_for($value)
{
return Create::promiseFor($value);
}
/**
* Creates a rejected promise for a reason if the reason is not a promise. If
* the provided reason is a promise, then it is returned as-is.
*
* @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*
* @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
*/
function rejection_for($reason)
{
return Create::rejectionFor($reason);
}
/**
* Create an exception for a rejected promise value.
*
* @param mixed $reason
*
* @return \Exception|\Throwable
*
* @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
*/
function exception_for($reason)
{
return Create::exceptionFor($reason);
}
/**
* Returns an iterator for the given value.
*
* @param mixed $value
*
* @return \Iterator
*
* @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
*/
function iter_for($value)
{
return Create::iterFor($value);
}
/**
* Synchronously waits on a promise to resolve and returns an inspection state
* array.
*
* Returns a state associative array containing a "state" key mapping to a
* valid promise state. If the state of the promise is "fulfilled", the array
* will contain a "value" key mapping to the fulfilled value of the promise. If
* the promise is rejected, the array will contain a "reason" key mapping to
* the rejection reason of the promise.
*
* @param PromiseInterface $promise Promise or value.
*
* @return array
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
*/
function inspect(PromiseInterface $promise)
{
return Utils::inspect($promise);
}
/**
* Waits on all of the provided promises, but does not unwrap rejected promises
* as thrown exception.
*
* Returns an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
*/
function inspect_all($promises)
{
return Utils::inspectAll($promises);
}
/**
* Waits on all of the provided promises and returns the fulfilled values.
*
* Returns an array that contains the value of each promise (in the same order
* the promises were provided). An exception is thrown if any of the promises
* are rejected.
*
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
*
* @return array
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*
* @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
*/
function unwrap($promises)
{
return Utils::unwrap($promises);
}
/**
* Given an array of promises, return a promise that is fulfilled when all the
* items in the array are fulfilled.
*
* The promise's fulfillment value is an array with fulfillment values at
* respective positions to the original array. If any promise in the array
* rejects, the returned promise is rejected with the rejection reason.
*
* @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*
* @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
*/
function all($promises, $recursive = false)
{
return Utils::all($promises, $recursive);
}
/**
* Initiate a competitive race between multiple promises or values (values will
* become immediately fulfilled promises).
*
* When count amount of promises have been fulfilled, the returned promise is
* fulfilled with an array that contains the fulfillment values of the winners
* in order of resolution.
*
* This promise is rejected with a {@see AggregateException} if the number of
* fulfilled promises is less than the desired $count.
*
* @param int $count Total number of promises.
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
*/
function some($count, $promises)
{
return Utils::some($count, $promises);
}
/**
* Like some(), with 1 as count. However, if the promise fulfills, the
* fulfillment value is not an array of 1 but the value directly.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
*/
function any($promises)
{
return Utils::any($promises);
}
/**
* Returns a promise that is fulfilled when all of the provided promises have
* been fulfilled or rejected.
*
* The returned promise is fulfilled with an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
*/
function settle($promises)
{
return Utils::settle($promises);
}
/**
* Given an iterator that yields promises or values, returns a promise that is
* fulfilled with a null value when the iterator has been consumed or the
* aggregate promise has been fulfilled or rejected.
*
* $onFulfilled is a function that accepts the fulfilled value, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* $onRejected is a function that accepts the rejection reason, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
*/
function each(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
) {
return Each::of($iterable, $onFulfilled, $onRejected);
}
/**
* Like each, but only allows a certain number of outstanding promises at any
* given time.
*
* $concurrency may be an integer or a function that accepts the number of
* pending promises and returns a numeric concurrency limit value to allow for
* dynamic a concurrency size.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
*/
function each_limit(
$iterable,
$concurrency,
callable $onFulfilled = null,
callable $onRejected = null
) {
return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
}
/**
* Like each_limit, but ensures that no promise in the given $iterable argument
* is rejected. If any promise is rejected, then the aggregate promise is
* rejected with the encountered rejection.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
*
* @return PromiseInterface
*
* @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
*/
function each_limit_all(
$iterable,
$concurrency,
callable $onFulfilled = null
) {
return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
}
/**
* Returns true if a promise is fulfilled.
*
* @return bool
*
* @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
*/
function is_fulfilled(PromiseInterface $promise)
{
return Is::fulfilled($promise);
}
/**
* Returns true if a promise is rejected.
*
* @return bool
*
* @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
*/
function is_rejected(PromiseInterface $promise)
{
return Is::rejected($promise);
}
/**
* Returns true if a promise is fulfilled or rejected.
*
* @return bool
*
* @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
*/
function is_settled(PromiseInterface $promise)
{
return Is::settled($promise);
}
/**
* Create a new coroutine.
*
* @see Coroutine
*
* @return PromiseInterface
*
* @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
*/
function coroutine(callable $generatorFn)
{
return Coroutine::of($generatorFn);
}

View File

@ -1,6 +0,0 @@
<?php
// Don't redefine the functions if included multiple times.
if (!function_exists('GuzzleHttp\Promise\promise_for')) {
require __DIR__ . '/functions.php';
}

View File

@ -5,7 +5,59 @@ 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/) 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased ## 2.6.2 - 2023-12-03
### Fixed
- Fixed another issue with the fact that PHP transforms numeric strings in array keys to ints
### Changed
- Updated links in docs to their canonical versions
- Replaced `call_user_func*` with native calls
## 2.6.1 - 2023-08-27
### Fixed
- Properly handle the fact that PHP transforms numeric strings in array keys to ints
## 2.6.0 - 2023-08-03
### Changed
- Updated the mime type map to add some new entries, fix a couple of invalid entries, and remove an invalid entry
- Fallback to `application/octet-stream` if we are unable to guess the content type for a multipart file upload
## 2.5.1 - 2023-08-03
### Fixed
- Corrected mime type for `.acc` files to `audio/aac`
### Changed
- PHP 8.3 support
## 2.5.0 - 2023-04-17
### Changed
- Adjusted `psr/http-message` version constraint to `^1.1 || ^2.0`
## 2.4.5 - 2023-04-17
### Fixed
- Prevent possible warnings on unset variables in `ServerRequest::normalizeNestedFileSpec`
- Fixed `Message::bodySummary` when `preg_match` fails
- Fixed header validation issue
## 2.4.4 - 2023-03-09
### Changed
- Removed the need for `AllowDynamicProperties` in `LazyOpenStream`
## 2.4.3 - 2022-10-26 ## 2.4.3 - 2022-10-26

View File

@ -8,12 +8,26 @@ functionality like query string parsing.
![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg) ![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
# Stream implementation ## Features
This package comes with a number of stream implementations and stream This package comes with a number of stream implementations and stream
decorators. decorators.
## Installation
```shell
composer require guzzlehttp/psr7
```
## Version Guidance
| Version | Status | PHP Version |
|---------|---------------------|--------------|
| 1.x | Security fixes only | >=5.4,<8.1 |
| 2.x | Latest | >=7.2.5,<8.4 |
## AppendStream ## AppendStream
`GuzzleHttp\Psr7\AppendStream` `GuzzleHttp\Psr7\AppendStream`
@ -245,6 +259,8 @@ class EofCallbackStream implements StreamInterface
private $callback; private $callback;
private $stream;
public function __construct(StreamInterface $stream, callable $cb) public function __construct(StreamInterface $stream, callable $cb)
{ {
$this->stream = $stream; $this->stream = $stream;
@ -257,7 +273,7 @@ class EofCallbackStream implements StreamInterface
// Invoke the callback when EOF is hit. // Invoke the callback when EOF is hit.
if ($this->eof()) { if ($this->eof()) {
call_user_func($this->callback); ($this->callback)();
} }
return $result; return $result;
@ -621,7 +637,7 @@ this library also provides additional functionality when working with URIs as st
An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference. An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI, An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
the base URI. Relative references can be divided into several forms according to the base URI. Relative references can be divided into several forms according to
[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2): [RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
- network-path references, e.g. `//example.com/path` - network-path references, e.g. `//example.com/path`
- absolute-path references, e.g. `/path` - absolute-path references, e.g. `/path`
@ -680,8 +696,8 @@ or the standard port. This method can be used independently of the implementatio
`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string` `public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
Composes a URI reference string from its various components according to Composes a URI reference string from its various components according to
[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called [RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`. to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
### `GuzzleHttp\Psr7\Uri::fromParts` ### `GuzzleHttp\Psr7\Uri::fromParts`
@ -725,8 +741,8 @@ Determines if a modified URL should be considered cross-origin with respect to a
## Reference Resolution ## Reference Resolution
`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according `GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
do when resolving a link in a website based on the current request URI. browsers do when resolving a link in a website based on the current request URI.
### `GuzzleHttp\Psr7\UriResolver::resolve` ### `GuzzleHttp\Psr7\UriResolver::resolve`
@ -739,7 +755,7 @@ Converts the relative URI into a new URI that is resolved against the base URI.
`public static function removeDotSegments(string $path): string` `public static function removeDotSegments(string $path): string`
Removes dot segments from a path and returns the new path according to Removes dot segments from a path and returns the new path according to
[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4). [RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
### `GuzzleHttp\Psr7\UriResolver::relativize` ### `GuzzleHttp\Psr7\UriResolver::relativize`
@ -765,7 +781,7 @@ echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // pr
## Normalization and Comparison ## Normalization and Comparison
`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to `GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6). [RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
### `GuzzleHttp\Psr7\UriNormalizer::normalize` ### `GuzzleHttp\Psr7\UriNormalizer::normalize`
@ -847,14 +863,6 @@ This of course assumes they will be resolved against the same base URI. If this
equivalence or difference of relative references does not mean anything. equivalence or difference of relative references does not mean anything.
## Version Guidance
| Version | Status | PHP Version |
|---------|----------------|------------------|
| 1.x | Security fixes | >=5.4,<8.1 |
| 2.x | Latest | ^7.2.5 \|\| ^8.0 |
## Security ## Security
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information. If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.

View File

@ -52,7 +52,7 @@
"require": { "require": {
"php": "^7.2.5 || ^8.0", "php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"psr/http-message": "^1.0", "psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0" "ralouphie/getallheaders": "^3.0"
}, },
"provide": { "provide": {
@ -60,9 +60,9 @@
"psr/http-message-implementation": "1.0" "psr/http-message-implementation": "1.0"
}, },
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1", "bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "^0.9", "http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.29 || ^9.5.23" "phpunit/phpunit": "^8.5.36 || ^9.6.15"
}, },
"suggest": { "suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@ -81,9 +81,6 @@
"bamarni-bin": { "bamarni-bin": {
"bin-links": true, "bin-links": true,
"forward-command": false "forward-command": false
},
"branch-alias": {
"dev-master": "2.4-dev"
} }
}, },
"config": { "config": {

View File

@ -40,12 +40,14 @@ final class AppendStream implements StreamInterface
{ {
try { try {
$this->rewind(); $this->rewind();
return $this->getContents(); return $this->getContents();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if (\PHP_VERSION_ID >= 70400) { if (\PHP_VERSION_ID >= 70400) {
throw $e; throw $e;
} }
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; return '';
} }
} }
@ -138,9 +140,9 @@ final class AppendStream implements StreamInterface
public function eof(): bool public function eof(): bool
{ {
return !$this->streams || return !$this->streams
($this->current >= count($this->streams) - 1 && || ($this->current >= count($this->streams) - 1
$this->streams[$this->current]->eof()); && $this->streams[$this->current]->eof());
} }
public function rewind(): void public function rewind(): void
@ -197,7 +199,7 @@ final class AppendStream implements StreamInterface
if ($this->current === $total) { if ($this->current === $total) {
break; break;
} }
$this->current++; ++$this->current;
} }
$result = $this->streams[$this->current]->read($remaining); $result = $this->streams[$this->current]->read($remaining);
@ -237,8 +239,6 @@ final class AppendStream implements StreamInterface
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)

View File

@ -134,8 +134,6 @@ final class BufferStream implements StreamInterface
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)

View File

@ -18,7 +18,7 @@ final class FnStream implements StreamInterface
private const SLOTS = [ private const SLOTS = [
'__toString', 'close', 'detach', 'rewind', '__toString', 'close', 'detach', 'rewind',
'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write', 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
'isReadable', 'read', 'getContents', 'getMetadata' 'isReadable', 'read', 'getContents', 'getMetadata',
]; ];
/** @var array<string, callable> */ /** @var array<string, callable> */
@ -54,7 +54,7 @@ final class FnStream implements StreamInterface
public function __destruct() public function __destruct()
{ {
if (isset($this->_fn_close)) { if (isset($this->_fn_close)) {
call_user_func($this->_fn_close); ($this->_fn_close)();
} }
} }
@ -93,88 +93,88 @@ final class FnStream implements StreamInterface
public function __toString(): string public function __toString(): string
{ {
try { try {
return call_user_func($this->_fn___toString); /** @var string */
return ($this->_fn___toString)();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if (\PHP_VERSION_ID >= 70400) { if (\PHP_VERSION_ID >= 70400) {
throw $e; throw $e;
} }
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; return '';
} }
} }
public function close(): void public function close(): void
{ {
call_user_func($this->_fn_close); ($this->_fn_close)();
} }
public function detach() public function detach()
{ {
return call_user_func($this->_fn_detach); return ($this->_fn_detach)();
} }
public function getSize(): ?int public function getSize(): ?int
{ {
return call_user_func($this->_fn_getSize); return ($this->_fn_getSize)();
} }
public function tell(): int public function tell(): int
{ {
return call_user_func($this->_fn_tell); return ($this->_fn_tell)();
} }
public function eof(): bool public function eof(): bool
{ {
return call_user_func($this->_fn_eof); return ($this->_fn_eof)();
} }
public function isSeekable(): bool public function isSeekable(): bool
{ {
return call_user_func($this->_fn_isSeekable); return ($this->_fn_isSeekable)();
} }
public function rewind(): void public function rewind(): void
{ {
call_user_func($this->_fn_rewind); ($this->_fn_rewind)();
} }
public function seek($offset, $whence = SEEK_SET): void public function seek($offset, $whence = SEEK_SET): void
{ {
call_user_func($this->_fn_seek, $offset, $whence); ($this->_fn_seek)($offset, $whence);
} }
public function isWritable(): bool public function isWritable(): bool
{ {
return call_user_func($this->_fn_isWritable); return ($this->_fn_isWritable)();
} }
public function write($string): int public function write($string): int
{ {
return call_user_func($this->_fn_write, $string); return ($this->_fn_write)($string);
} }
public function isReadable(): bool public function isReadable(): bool
{ {
return call_user_func($this->_fn_isReadable); return ($this->_fn_isReadable)();
} }
public function read($length): string public function read($length): string
{ {
return call_user_func($this->_fn_read, $length); return ($this->_fn_read)($length);
} }
public function getContents(): string public function getContents(): string
{ {
return call_user_func($this->_fn_getContents); return ($this->_fn_getContents)();
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)
{ {
return call_user_func($this->_fn_getMetadata, $key); return ($this->_fn_getMetadata)($key);
} }
} }

View File

@ -22,7 +22,7 @@ final class Header
foreach ((array) $header as $value) { foreach ((array) $header as $value) {
foreach (self::splitList($value) as $val) { foreach (self::splitList($value) as $val) {
$part = []; $part = [];
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) {
if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
$m = $matches[0]; $m = $matches[0];
if (isset($m[1])) { if (isset($m[1])) {
@ -89,7 +89,7 @@ final class Header
$v = ''; $v = '';
$isQuoted = false; $isQuoted = false;
$isEscaped = false; $isEscaped = false;
for ($i = 0, $max = \strlen($value); $i < $max; $i++) { for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
if ($isEscaped) { if ($isEscaped) {
$v .= $value[$i]; $v .= $value[$i];
$isEscaped = false; $isEscaped = false;

View File

@ -23,13 +23,7 @@ use Psr\Http\Message\UriInterface;
* Note: in consuming code it is recommended to require the implemented interfaces * Note: in consuming code it is recommended to require the implemented interfaces
* and inject the instance of this class multiple times. * and inject the instance of this class multiple times.
*/ */
final class HttpFactory implements final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
RequestFactoryInterface,
ResponseFactoryInterface,
ServerRequestFactoryInterface,
StreamFactoryInterface,
UploadedFileFactoryInterface,
UriFactoryInterface
{ {
public function createUploadedFile( public function createUploadedFile(
StreamInterface $stream, StreamInterface $stream,

View File

@ -13,9 +13,9 @@ use Psr\Http\Message\StreamInterface;
* then appends the zlib.inflate filter. The stream is then converted back * then appends the zlib.inflate filter. The stream is then converted back
* to a Guzzle stream resource to be used as a Guzzle stream. * to a Guzzle stream resource to be used as a Guzzle stream.
* *
* @link http://tools.ietf.org/html/rfc1950 * @see https://datatracker.ietf.org/doc/html/rfc1950
* @link http://tools.ietf.org/html/rfc1952 * @see https://datatracker.ietf.org/doc/html/rfc1952
* @link http://php.net/manual/en/filters.compression.php * @see https://www.php.net/manual/en/filters.compression.php
*/ */
final class InflateStream implements StreamInterface final class InflateStream implements StreamInterface
{ {
@ -28,7 +28,7 @@ final class InflateStream implements StreamInterface
{ {
$resource = StreamWrapper::getResource($stream); $resource = StreamWrapper::getResource($stream);
// Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
// See http://www.zlib.net/manual.html#Advanced definition of inflateInit2 // See https://www.zlib.net/manual.html#Advanced definition of inflateInit2
// "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection" // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
// Default window size is 15. // Default window size is 15.
stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]); stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);

View File

@ -10,7 +10,6 @@ use Psr\Http\Message\StreamInterface;
* Lazily reads or writes to a file that is opened only after an IO operation * Lazily reads or writes to a file that is opened only after an IO operation
* take place on the stream. * take place on the stream.
*/ */
#[\AllowDynamicProperties]
final class LazyOpenStream implements StreamInterface final class LazyOpenStream implements StreamInterface
{ {
use StreamDecoratorTrait; use StreamDecoratorTrait;
@ -21,6 +20,11 @@ final class LazyOpenStream implements StreamInterface
/** @var string */ /** @var string */
private $mode; private $mode;
/**
* @var StreamInterface
*/
private $stream;
/** /**
* @param string $filename File to lazily open * @param string $filename File to lazily open
* @param string $mode fopen mode to use when opening the stream * @param string $mode fopen mode to use when opening the stream
@ -29,6 +33,10 @@ final class LazyOpenStream implements StreamInterface
{ {
$this->filename = $filename; $this->filename = $filename;
$this->mode = $mode; $this->mode = $mode;
// unsetting the property forces the first access to go through
// __get().
unset($this->stream);
} }
/** /**

View File

@ -33,7 +33,7 @@ final class Message
} }
foreach ($message->getHeaders() as $name => $values) { foreach ($message->getHeaders() as $name => $values) {
if (strtolower($name) === 'set-cookie') { if (is_string($name) && strtolower($name) === 'set-cookie') {
foreach ($values as $value) { foreach ($values as $value) {
$msg .= "\r\n{$name}: ".$value; $msg .= "\r\n{$name}: ".$value;
} }
@ -77,7 +77,7 @@ final class Message
// Matches any printable character, including unicode characters: // Matches any printable character, including unicode characters:
// letters, marks, numbers, punctuation, spacing, and separators. // letters, marks, numbers, punctuation, spacing, and separators.
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) { if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
return null; return null;
} }
@ -146,7 +146,7 @@ final class Message
// If these aren't the same, then one line didn't match and there's an invalid header. // If these aren't the same, then one line didn't match and there's an invalid header.
if ($count !== substr_count($rawHeaders, "\n")) { if ($count !== substr_count($rawHeaders, "\n")) {
// Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4 // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
} }
@ -227,9 +227,9 @@ final class Message
public static function parseResponse(string $message): ResponseInterface public static function parseResponse(string $message): ResponseInterface
{ {
$data = self::parseMessage($message); $data = self::parseMessage($message);
// According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
// between status-code and reason-phrase is required. But browsers accept // the space between status-code and reason-phrase is required. But
// responses without space and reason as well. // browsers accept responses without space and reason as well.
if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']); throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
} }

View File

@ -12,10 +12,10 @@ use Psr\Http\Message\StreamInterface;
*/ */
trait MessageTrait trait MessageTrait
{ {
/** @var array<string, string[]> Map of all registered headers, as original name => array of values */ /** @var string[][] Map of all registered headers, as original name => array of values */
private $headers = []; private $headers = [];
/** @var array<string, string> Map of lowercase header name => original name at registration */ /** @var string[] Map of lowercase header name => original name at registration */
private $headerNames = []; private $headerNames = [];
/** @var string */ /** @var string */
@ -37,6 +37,7 @@ trait MessageTrait
$new = clone $this; $new = clone $this;
$new->protocol = $version; $new->protocol = $version;
return $new; return $new;
} }
@ -135,11 +136,12 @@ trait MessageTrait
$new = clone $this; $new = clone $this;
$new->stream = $body; $new->stream = $body;
return $new; return $new;
} }
/** /**
* @param array<string|int, string|string[]> $headers * @param (string|string[])[] $headers
*/ */
private function setHeaders(array $headers): void private function setHeaders(array $headers): void
{ {
@ -191,7 +193,7 @@ trait MessageTrait
* *
* @return string[] Trimmed header values * @return string[] Trimmed header values
* *
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4 * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
*/ */
private function trimAndValidateHeaderValues(array $values): array private function trimAndValidateHeaderValues(array $values): array
{ {
@ -211,7 +213,7 @@ trait MessageTrait
} }
/** /**
* @see https://tools.ietf.org/html/rfc7230#section-3.2 * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
* *
* @param mixed $header * @param mixed $header
*/ */
@ -224,18 +226,15 @@ trait MessageTrait
)); ));
} }
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) { if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
sprintf( sprintf('"%s" is not valid header name.', $header)
'"%s" is not valid header name',
$header
)
); );
} }
} }
/** /**
* @see https://tools.ietf.org/html/rfc7230#section-3.2 * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
* *
* field-value = *( field-content / obs-fold ) * field-value = *( field-content / obs-fold )
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
@ -257,8 +256,10 @@ trait MessageTrait
// Clients must not send a request with line folding and a server sending folded headers is // Clients must not send a request with line folding and a server sending folded headers is
// likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
// folding is not likely to break any legitimate use case. // folding is not likely to break any legitimate use case.
if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) { if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value)); throw new \InvalidArgumentException(
sprintf('"%s" is not valid header value.', $value)
);
} }
} }
} }

View File

@ -18,7 +18,7 @@ final class MimeType
'7zip' => 'application/x-7z-compressed', '7zip' => 'application/x-7z-compressed',
'123' => 'application/vnd.lotus-1-2-3', '123' => 'application/vnd.lotus-1-2-3',
'aab' => 'application/x-authorware-bin', 'aab' => 'application/x-authorware-bin',
'aac' => 'audio/x-acc', 'aac' => 'audio/aac',
'aam' => 'application/x-authorware-map', 'aam' => 'application/x-authorware-map',
'aas' => 'application/x-authorware-seg', 'aas' => 'application/x-authorware-seg',
'abw' => 'application/x-abiword', 'abw' => 'application/x-abiword',
@ -29,6 +29,7 @@ final class MimeType
'acu' => 'application/vnd.acucobol', 'acu' => 'application/vnd.acucobol',
'acutc' => 'application/vnd.acucorp', 'acutc' => 'application/vnd.acucorp',
'adp' => 'audio/adpcm', 'adp' => 'audio/adpcm',
'adts' => 'audio/aac',
'aep' => 'application/vnd.audiograph', 'aep' => 'application/vnd.audiograph',
'afm' => 'application/x-font-type1', 'afm' => 'application/x-font-type1',
'afp' => 'application/vnd.ibm.modcap', 'afp' => 'application/vnd.ibm.modcap',
@ -41,11 +42,16 @@ final class MimeType
'air' => 'application/vnd.adobe.air-application-installer-package+zip', 'air' => 'application/vnd.adobe.air-application-installer-package+zip',
'ait' => 'application/vnd.dvb.ait', 'ait' => 'application/vnd.dvb.ait',
'ami' => 'application/vnd.amiga.ami', 'ami' => 'application/vnd.amiga.ami',
'aml' => 'application/automationml-aml+xml',
'amlx' => 'application/automationml-amlx+zip',
'amr' => 'audio/amr', 'amr' => 'audio/amr',
'apk' => 'application/vnd.android.package-archive', 'apk' => 'application/vnd.android.package-archive',
'apng' => 'image/apng', 'apng' => 'image/apng',
'appcache' => 'text/cache-manifest', 'appcache' => 'text/cache-manifest',
'appinstaller' => 'application/appinstaller',
'application' => 'application/x-ms-application', 'application' => 'application/x-ms-application',
'appx' => 'application/appx',
'appxbundle' => 'application/appxbundle',
'apr' => 'application/vnd.lotus-approach', 'apr' => 'application/vnd.lotus-approach',
'arc' => 'application/x-freearc', 'arc' => 'application/x-freearc',
'arj' => 'application/x-arj', 'arj' => 'application/x-arj',
@ -90,6 +96,7 @@ final class MimeType
'bpk' => 'application/octet-stream', 'bpk' => 'application/octet-stream',
'bpmn' => 'application/octet-stream', 'bpmn' => 'application/octet-stream',
'bsp' => 'model/vnd.valve.source.compiled-map', 'bsp' => 'model/vnd.valve.source.compiled-map',
'btf' => 'image/prs.btif',
'btif' => 'image/prs.btif', 'btif' => 'image/prs.btif',
'buffer' => 'application/octet-stream', 'buffer' => 'application/octet-stream',
'bz' => 'application/x-bzip', 'bz' => 'application/x-bzip',
@ -141,6 +148,7 @@ final class MimeType
'cjs' => 'application/node', 'cjs' => 'application/node',
'cla' => 'application/vnd.claymore', 'cla' => 'application/vnd.claymore',
'class' => 'application/octet-stream', 'class' => 'application/octet-stream',
'cld' => 'model/vnd.cld',
'clkk' => 'application/vnd.crick.clicker.keyboard', 'clkk' => 'application/vnd.crick.clicker.keyboard',
'clkp' => 'application/vnd.crick.clicker.palette', 'clkp' => 'application/vnd.crick.clicker.palette',
'clkt' => 'application/vnd.crick.clicker.template', 'clkt' => 'application/vnd.crick.clicker.template',
@ -175,6 +183,7 @@ final class MimeType
'csv' => 'text/csv', 'csv' => 'text/csv',
'cu' => 'application/cu-seeme', 'cu' => 'application/cu-seeme',
'curl' => 'text/vnd.curl', 'curl' => 'text/vnd.curl',
'cwl' => 'application/cwl',
'cww' => 'application/prs.cww', 'cww' => 'application/prs.cww',
'cxt' => 'application/x-director', 'cxt' => 'application/x-director',
'cxx' => 'text/x-c', 'cxx' => 'text/x-c',
@ -197,6 +206,7 @@ final class MimeType
'der' => 'application/x-x509-ca-cert', 'der' => 'application/x-x509-ca-cert',
'dfac' => 'application/vnd.dreamfactory', 'dfac' => 'application/vnd.dreamfactory',
'dgc' => 'application/x-dgc-compressed', 'dgc' => 'application/x-dgc-compressed',
'dib' => 'image/bmp',
'dic' => 'text/x-c', 'dic' => 'text/x-c',
'dir' => 'application/x-director', 'dir' => 'application/x-director',
'dis' => 'application/vnd.mobius.dis', 'dis' => 'application/vnd.mobius.dis',
@ -219,6 +229,7 @@ final class MimeType
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'dp' => 'application/vnd.osgi.dp', 'dp' => 'application/vnd.osgi.dp',
'dpg' => 'application/vnd.dpgraph', 'dpg' => 'application/vnd.dpgraph',
'dpx' => 'image/dpx',
'dra' => 'audio/vnd.dra', 'dra' => 'audio/vnd.dra',
'drle' => 'image/dicom-rle', 'drle' => 'image/dicom-rle',
'dsc' => 'text/prs.lines.tag', 'dsc' => 'text/prs.lines.tag',
@ -255,7 +266,6 @@ final class MimeType
'eot' => 'application/vnd.ms-fontobject', 'eot' => 'application/vnd.ms-fontobject',
'eps' => 'application/postscript', 'eps' => 'application/postscript',
'epub' => 'application/epub+zip', 'epub' => 'application/epub+zip',
'es' => 'application/ecmascript',
'es3' => 'application/vnd.eszigno3+xml', 'es3' => 'application/vnd.eszigno3+xml',
'esa' => 'application/vnd.osgi.subsystem', 'esa' => 'application/vnd.osgi.subsystem',
'esf' => 'application/vnd.epson.esf', 'esf' => 'application/vnd.epson.esf',
@ -448,6 +458,7 @@ final class MimeType
'jsonld' => 'application/ld+json', 'jsonld' => 'application/ld+json',
'jsonml' => 'application/jsonml+json', 'jsonml' => 'application/jsonml+json',
'jsx' => 'text/jsx', 'jsx' => 'text/jsx',
'jt' => 'model/jt',
'jxr' => 'image/jxr', 'jxr' => 'image/jxr',
'jxra' => 'image/jxra', 'jxra' => 'image/jxra',
'jxrs' => 'image/jxrs', 'jxrs' => 'image/jxrs',
@ -552,7 +563,7 @@ final class MimeType
'mime' => 'message/rfc822', 'mime' => 'message/rfc822',
'mj2' => 'video/mj2', 'mj2' => 'video/mj2',
'mjp2' => 'video/mj2', 'mjp2' => 'video/mj2',
'mjs' => 'application/javascript', 'mjs' => 'text/javascript',
'mk3d' => 'video/x-matroska', 'mk3d' => 'video/x-matroska',
'mka' => 'audio/x-matroska', 'mka' => 'audio/x-matroska',
'mkd' => 'text/x-markdown', 'mkd' => 'text/x-markdown',
@ -602,6 +613,8 @@ final class MimeType
'msg' => 'application/vnd.ms-outlook', 'msg' => 'application/vnd.ms-outlook',
'msh' => 'model/mesh', 'msh' => 'model/mesh',
'msi' => 'application/x-msdownload', 'msi' => 'application/x-msdownload',
'msix' => 'application/msix',
'msixbundle' => 'application/msixbundle',
'msl' => 'application/vnd.mobius.msl', 'msl' => 'application/vnd.mobius.msl',
'msm' => 'application/octet-stream', 'msm' => 'application/octet-stream',
'msp' => 'application/octet-stream', 'msp' => 'application/octet-stream',
@ -775,6 +788,8 @@ final class MimeType
'pvb' => 'application/vnd.3gpp.pic-bw-var', 'pvb' => 'application/vnd.3gpp.pic-bw-var',
'pwn' => 'application/vnd.3m.post-it-notes', 'pwn' => 'application/vnd.3m.post-it-notes',
'pya' => 'audio/vnd.ms-playready.media.pya', 'pya' => 'audio/vnd.ms-playready.media.pya',
'pyo' => 'model/vnd.pytha.pyox',
'pyox' => 'model/vnd.pytha.pyox',
'pyv' => 'video/vnd.ms-playready.media.pyv', 'pyv' => 'video/vnd.ms-playready.media.pyv',
'qam' => 'application/vnd.epson.quickanime', 'qam' => 'application/vnd.epson.quickanime',
'qbo' => 'application/vnd.intu.qbo', 'qbo' => 'application/vnd.intu.qbo',
@ -923,10 +938,12 @@ final class MimeType
'st' => 'application/vnd.sailingtracker.track', 'st' => 'application/vnd.sailingtracker.track',
'stc' => 'application/vnd.sun.xml.calc.template', 'stc' => 'application/vnd.sun.xml.calc.template',
'std' => 'application/vnd.sun.xml.draw.template', 'std' => 'application/vnd.sun.xml.draw.template',
'step' => 'application/STEP',
'stf' => 'application/vnd.wt.stf', 'stf' => 'application/vnd.wt.stf',
'sti' => 'application/vnd.sun.xml.impress.template', 'sti' => 'application/vnd.sun.xml.impress.template',
'stk' => 'application/hyperstudio', 'stk' => 'application/hyperstudio',
'stl' => 'model/stl', 'stl' => 'model/stl',
'stp' => 'application/STEP',
'stpx' => 'model/step+xml', 'stpx' => 'model/step+xml',
'stpxz' => 'model/step-xml+zip', 'stpxz' => 'model/step-xml+zip',
'stpz' => 'model/step+zip', 'stpz' => 'model/step+zip',
@ -1013,10 +1030,12 @@ final class MimeType
'ulx' => 'application/x-glulx', 'ulx' => 'application/x-glulx',
'umj' => 'application/vnd.umajin', 'umj' => 'application/vnd.umajin',
'unityweb' => 'application/vnd.unity', 'unityweb' => 'application/vnd.unity',
'uo' => 'application/vnd.uoml+xml',
'uoml' => 'application/vnd.uoml+xml', 'uoml' => 'application/vnd.uoml+xml',
'uri' => 'text/uri-list', 'uri' => 'text/uri-list',
'uris' => 'text/uri-list', 'uris' => 'text/uri-list',
'urls' => 'text/uri-list', 'urls' => 'text/uri-list',
'usda' => 'model/vnd.usda',
'usdz' => 'model/vnd.usdz+zip', 'usdz' => 'model/vnd.usdz+zip',
'ustar' => 'application/x-ustar', 'ustar' => 'application/x-ustar',
'utz' => 'application/vnd.uiq.theme', 'utz' => 'application/vnd.uiq.theme',
@ -1096,6 +1115,7 @@ final class MimeType
'webmanifest' => 'application/manifest+json', 'webmanifest' => 'application/manifest+json',
'webp' => 'image/webp', 'webp' => 'image/webp',
'wg' => 'application/vnd.pmi.widget', 'wg' => 'application/vnd.pmi.widget',
'wgsl' => 'text/wgsl',
'wgt' => 'application/widget', 'wgt' => 'application/widget',
'wif' => 'application/watcherinfo+xml', 'wif' => 'application/watcherinfo+xml',
'wks' => 'application/vnd.ms-works', 'wks' => 'application/vnd.ms-works',
@ -1150,9 +1170,10 @@ final class MimeType
'xel' => 'application/xcap-el+xml', 'xel' => 'application/xcap-el+xml',
'xenc' => 'application/xenc+xml', 'xenc' => 'application/xenc+xml',
'xer' => 'application/patch-ops-error+xml', 'xer' => 'application/patch-ops-error+xml',
'xfdf' => 'application/vnd.adobe.xfdf', 'xfdf' => 'application/xfdf',
'xfdl' => 'application/vnd.xfdl', 'xfdl' => 'application/vnd.xfdl',
'xht' => 'application/xhtml+xml', 'xht' => 'application/xhtml+xml',
'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
'xhtml' => 'application/xhtml+xml', 'xhtml' => 'application/xhtml+xml',
'xhvml' => 'application/xv+xml', 'xhvml' => 'application/xv+xml',
'xif' => 'image/vnd.xiff', 'xif' => 'image/vnd.xiff',
@ -1183,6 +1204,7 @@ final class MimeType
'xpw' => 'application/vnd.intercon.formnet', 'xpw' => 'application/vnd.intercon.formnet',
'xpx' => 'application/vnd.intercon.formnet', 'xpx' => 'application/vnd.intercon.formnet',
'xsd' => 'application/xml', 'xsd' => 'application/xml',
'xsf' => 'application/prs.xsf+xml',
'xsl' => 'application/xml', 'xsl' => 'application/xml',
'xslt' => 'application/xslt+xml', 'xslt' => 'application/xslt+xml',
'xsm' => 'application/vnd.syncml+xml', 'xsm' => 'application/vnd.syncml+xml',
@ -1218,7 +1240,7 @@ final class MimeType
/** /**
* Determines the mimetype of a file by looking at its extension. * Determines the mimetype of a file by looking at its extension.
* *
* @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
*/ */
public static function fromFilename(string $filename): ?string public static function fromFilename(string $filename): ?string
{ {
@ -1228,7 +1250,7 @@ final class MimeType
/** /**
* Maps a file extensions to a mimetype. * Maps a file extensions to a mimetype.
* *
* @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
*/ */
public static function fromExtension(string $extension): ?string public static function fromExtension(string $extension): ?string
{ {

View File

@ -51,7 +51,7 @@ final class MultipartStream implements StreamInterface
/** /**
* Get the headers needed before transferring the content of a POST file * Get the headers needed before transferring the content of a POST file
* *
* @param array<string, string> $headers * @param string[] $headers
*/ */
private function getHeaders(array $headers): string private function getHeaders(array $headers): string
{ {
@ -72,7 +72,7 @@ final class MultipartStream implements StreamInterface
foreach ($elements as $element) { foreach ($elements as $element) {
if (!is_array($element)) { if (!is_array($element)) {
throw new \UnexpectedValueException("An array is expected"); throw new \UnexpectedValueException('An array is expected');
} }
$this->addElement($stream, $element); $this->addElement($stream, $element);
} }
@ -112,10 +112,15 @@ final class MultipartStream implements StreamInterface
$stream->addStream(Utils::streamFor("\r\n")); $stream->addStream(Utils::streamFor("\r\n"));
} }
/**
* @param string[] $headers
*
* @return array{0: StreamInterface, 1: string[]}
*/
private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
{ {
// Set a default content-disposition header if one was no provided // Set a default content-disposition header if one was no provided
$disposition = $this->getHeader($headers, 'content-disposition'); $disposition = self::getHeader($headers, 'content-disposition');
if (!$disposition) { if (!$disposition) {
$headers['Content-Disposition'] = ($filename === '0' || $filename) $headers['Content-Disposition'] = ($filename === '0' || $filename)
? sprintf( ? sprintf(
@ -127,7 +132,7 @@ final class MultipartStream implements StreamInterface
} }
// Set a default content-length header if one was no provided // Set a default content-length header if one was no provided
$length = $this->getHeader($headers, 'content-length'); $length = self::getHeader($headers, 'content-length');
if (!$length) { if (!$length) {
if ($length = $stream->getSize()) { if ($length = $stream->getSize()) {
$headers['Content-Length'] = (string) $length; $headers['Content-Length'] = (string) $length;
@ -135,21 +140,22 @@ final class MultipartStream implements StreamInterface
} }
// Set a default Content-Type if one was not supplied // Set a default Content-Type if one was not supplied
$type = $this->getHeader($headers, 'content-type'); $type = self::getHeader($headers, 'content-type');
if (!$type && ($filename === '0' || $filename)) { if (!$type && ($filename === '0' || $filename)) {
if ($type = MimeType::fromFilename($filename)) { $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
$headers['Content-Type'] = $type;
}
} }
return [$stream, $headers]; return [$stream, $headers];
} }
private function getHeader(array $headers, string $key) /**
* @param string[] $headers
*/
private static function getHeader(array $headers, string $key): ?string
{ {
$lowercaseHeader = strtolower($key); $lowercaseHeader = strtolower($key);
foreach ($headers as $k => $v) { foreach ($headers as $k => $v) {
if (strtolower($k) === $lowercaseHeader) { if (strtolower((string) $k) === $lowercaseHeader) {
return $v; return $v;
} }
} }

View File

@ -18,7 +18,7 @@ use Psr\Http\Message\StreamInterface;
*/ */
final class PumpStream implements StreamInterface final class PumpStream implements StreamInterface
{ {
/** @var callable|null */ /** @var callable(int): (string|false|null)|null */
private $source; private $source;
/** @var int|null */ /** @var int|null */
@ -34,7 +34,7 @@ final class PumpStream implements StreamInterface
private $buffer; private $buffer;
/** /**
* @param callable(int): (string|null|false) $source Source of the stream data. The callable MAY * @param callable(int): (string|false|null) $source Source of the stream data. The callable MAY
* accept an integer argument used to control the * accept an integer argument used to control the
* amount of data to return. The callable MUST * amount of data to return. The callable MUST
* return a string when called, or false|null on error * return a string when called, or false|null on error
@ -60,6 +60,7 @@ final class PumpStream implements StreamInterface
throw $e; throw $e;
} }
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; return '';
} }
} }
@ -149,8 +150,6 @@ final class PumpStream implements StreamInterface
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)
@ -164,11 +163,12 @@ final class PumpStream implements StreamInterface
private function pump(int $length): void private function pump(int $length): void
{ {
if ($this->source) { if ($this->source !== null) {
do { do {
$data = call_user_func($this->source, $length); $data = ($this->source)($length);
if ($data === false || $data === null) { if ($data === false || $data === null) {
$this->source = null; $this->source = null;
return; return;
} }
$this->buffer->write($data); $this->buffer->write($data);

View File

@ -28,7 +28,7 @@ class Request implements RequestInterface
/** /**
* @param string $method HTTP method * @param string $method HTTP method
* @param string|UriInterface $uri URI * @param string|UriInterface $uri URI
* @param array<string, string|string[]> $headers Request headers * @param (string|string[])[] $headers Request headers
* @param string|resource|StreamInterface|null $body Request body * @param string|resource|StreamInterface|null $body Request body
* @param string $version Protocol version * @param string $version Protocol version
*/ */
@ -85,6 +85,7 @@ class Request implements RequestInterface
$new = clone $this; $new = clone $this;
$new->requestTarget = $requestTarget; $new->requestTarget = $requestTarget;
return $new; return $new;
} }
@ -98,6 +99,7 @@ class Request implements RequestInterface
$this->assertMethod($method); $this->assertMethod($method);
$new = clone $this; $new = clone $this;
$new->method = strtoupper($method); $new->method = strtoupper($method);
return $new; return $new;
} }
@ -141,7 +143,7 @@ class Request implements RequestInterface
$this->headerNames['host'] = 'Host'; $this->headerNames['host'] = 'Host';
} }
// Ensure Host is the first header. // Ensure Host is the first header.
// See: http://tools.ietf.org/html/rfc7230#section-5.4 // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
$this->headers = [$header => [$host]] + $this->headers; $this->headers = [$header => [$host]] + $this->headers;
} }

View File

@ -86,7 +86,7 @@ class Response implements ResponseInterface
/** /**
* @param int $status Status code * @param int $status Status code
* @param array<string, string|string[]> $headers Response headers * @param (string|string[])[] $headers Response headers
* @param string|resource|StreamInterface|null $body Response body * @param string|resource|StreamInterface|null $body Response body
* @param string $version Protocol version * @param string $version Protocol version
* @param string|null $reason Reason phrase (when empty a default will be used based on the status code) * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
@ -138,6 +138,7 @@ class Response implements ResponseInterface
$reasonPhrase = self::PHRASES[$new->statusCode]; $reasonPhrase = self::PHRASES[$new->statusCode];
} }
$new->reasonPhrase = (string) $reasonPhrase; $new->reasonPhrase = (string) $reasonPhrase;
return $new; return $new;
} }

View File

@ -14,7 +14,7 @@ final class Rfc7230
* *
* Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons. * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
* *
* @link https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15 * @see https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
* *
* @license https://github.com/amphp/http/blob/v1.0.1/LICENSE * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
*/ */

View File

@ -59,7 +59,7 @@ class ServerRequest extends Request implements ServerRequestInterface
/** /**
* @param string $method HTTP method * @param string $method HTTP method
* @param string|UriInterface $uri URI * @param string|UriInterface $uri URI
* @param array<string, string|string[]> $headers Request headers * @param (string|string[])[] $headers Request headers
* @param string|resource|StreamInterface|null $body Request body * @param string|resource|StreamInterface|null $body Request body
* @param string $version Protocol version * @param string $version Protocol version
* @param array $serverParams Typically the $_SERVER superglobal * @param array $serverParams Typically the $_SERVER superglobal
@ -144,10 +144,10 @@ class ServerRequest extends Request implements ServerRequestInterface
foreach (array_keys($files['tmp_name']) as $key) { foreach (array_keys($files['tmp_name']) as $key) {
$spec = [ $spec = [
'tmp_name' => $files['tmp_name'][$key], 'tmp_name' => $files['tmp_name'][$key],
'size' => $files['size'][$key], 'size' => $files['size'][$key] ?? null,
'error' => $files['error'][$key], 'error' => $files['error'][$key] ?? null,
'name' => $files['name'][$key], 'name' => $files['name'][$key] ?? null,
'type' => $files['type'][$key], 'type' => $files['type'][$key] ?? null,
]; ];
$normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
} }
@ -286,8 +286,6 @@ class ServerRequest extends Request implements ServerRequestInterface
} }
/** /**
* {@inheritdoc}
*
* @return array|object|null * @return array|object|null
*/ */
public function getParsedBody() public function getParsedBody()
@ -309,8 +307,6 @@ class ServerRequest extends Request implements ServerRequestInterface
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getAttribute($attribute, $default = null) public function getAttribute($attribute, $default = null)

View File

@ -12,8 +12,8 @@ use Psr\Http\Message\StreamInterface;
class Stream implements StreamInterface class Stream implements StreamInterface
{ {
/** /**
* @see http://php.net/manual/function.fopen.php * @see https://www.php.net/manual/en/function.fopen.php
* @see http://php.net/manual/en/function.gzopen.php * @see https://www.php.net/manual/en/function.gzopen.php
*/ */
private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/'; private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/'; private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
@ -80,12 +80,14 @@ class Stream implements StreamInterface
if ($this->isSeekable()) { if ($this->isSeekable()) {
$this->seek(0); $this->seek(0);
} }
return $this->getContents(); return $this->getContents();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if (\PHP_VERSION_ID >= 70400) { if (\PHP_VERSION_ID >= 70400) {
throw $e; throw $e;
} }
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; return '';
} }
} }
@ -145,6 +147,7 @@ class Stream implements StreamInterface
$stats = fstat($this->stream); $stats = fstat($this->stream);
if (is_array($stats) && isset($stats['size'])) { if (is_array($stats) && isset($stats['size'])) {
$this->size = $stats['size']; $this->size = $stats['size'];
return $this->size; return $this->size;
} }
@ -261,8 +264,6 @@ class Stream implements StreamInterface
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)

View File

@ -31,6 +31,7 @@ trait StreamDecoratorTrait
{ {
if ($name === 'stream') { if ($name === 'stream') {
$this->stream = $this->createStream(); $this->stream = $this->createStream();
return $this->stream; return $this->stream;
} }
@ -43,12 +44,14 @@ trait StreamDecoratorTrait
if ($this->isSeekable()) { if ($this->isSeekable()) {
$this->seek(0); $this->seek(0);
} }
return $this->getContents(); return $this->getContents();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if (\PHP_VERSION_ID >= 70400) { if (\PHP_VERSION_ID >= 70400) {
throw $e; throw $e;
} }
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; return '';
} }
} }
@ -67,7 +70,7 @@ trait StreamDecoratorTrait
{ {
/** @var callable $callable */ /** @var callable $callable */
$callable = [$this->stream, $method]; $callable = [$this->stream, $method];
$result = call_user_func_array($callable, $args); $result = ($callable)(...$args);
// Always return the wrapped object if the result is a return $this // Always return the wrapped object if the result is a return $this
return $result === $this->stream ? $this : $result; return $result === $this->stream ? $this : $result;
@ -79,8 +82,6 @@ trait StreamDecoratorTrait
} }
/** /**
* {@inheritdoc}
*
* @return mixed * @return mixed
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)

View File

@ -55,7 +55,7 @@ final class StreamWrapper
public static function createStreamContext(StreamInterface $stream) public static function createStreamContext(StreamInterface $stream)
{ {
return stream_context_create([ return stream_context_create([
'guzzle' => ['stream' => $stream] 'guzzle' => ['stream' => $stream],
]); ]);
} }
@ -115,14 +115,28 @@ final class StreamWrapper
*/ */
public function stream_cast(int $cast_as) public function stream_cast(int $cast_as)
{ {
$stream = clone($this->stream); $stream = clone $this->stream;
$resource = $stream->detach(); $resource = $stream->detach();
return $resource ?? false; return $resource ?? false;
} }
/** /**
* @return array<int|string, int> * @return array{
* dev: int,
* ino: int,
* mode: int,
* nlink: int,
* uid: int,
* gid: int,
* rdev: int,
* size: int,
* atime: int,
* mtime: int,
* ctime: int,
* blksize: int,
* blocks: int
* }
*/ */
public function stream_stat(): array public function stream_stat(): array
{ {
@ -131,7 +145,7 @@ final class StreamWrapper
'rb' => 33060, 'rb' => 33060,
'r+' => 33206, 'r+' => 33206,
'w' => 33188, 'w' => 33188,
'wb' => 33188 'wb' => 33188,
]; ];
return [ return [
@ -147,12 +161,26 @@ final class StreamWrapper
'mtime' => 0, 'mtime' => 0,
'ctime' => 0, 'ctime' => 0,
'blksize' => 0, 'blksize' => 0,
'blocks' => 0 'blocks' => 0,
]; ];
} }
/** /**
* @return array<int|string, int> * @return array{
* dev: int,
* ino: int,
* mode: int,
* nlink: int,
* uid: int,
* gid: int,
* rdev: int,
* size: int,
* atime: int,
* mtime: int,
* ctime: int,
* blksize: int,
* blocks: int
* }
*/ */
public function url_stat(string $path, int $flags): array public function url_stat(string $path, int $flags): array
{ {
@ -169,7 +197,7 @@ final class StreamWrapper
'mtime' => 0, 'mtime' => 0,
'ctime' => 0, 'ctime' => 0,
'blksize' => 0, 'blksize' => 0,
'blocks' => 0 'blocks' => 0,
]; ];
} }
} }

View File

@ -113,7 +113,7 @@ class UploadedFile implements UploadedFileInterface
$this->error = $error; $this->error = $error;
} }
private function isStringNotEmpty($param): bool private static function isStringNotEmpty($param): bool
{ {
return is_string($param) && false === empty($param); return is_string($param) && false === empty($param);
} }
@ -163,7 +163,7 @@ class UploadedFile implements UploadedFileInterface
{ {
$this->validateActive(); $this->validateActive();
if (false === $this->isStringNotEmpty($targetPath)) { if (false === self::isStringNotEmpty($targetPath)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
'Invalid path provided for move operation; must be a non-empty string' 'Invalid path provided for move operation; must be a non-empty string'
); );

View File

@ -41,14 +41,14 @@ class Uri implements UriInterface, \JsonSerializable
/** /**
* Unreserved characters for use in a regex. * Unreserved characters for use in a regex.
* *
* @link https://tools.ietf.org/html/rfc3986#section-2.3 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
*/ */
private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~'; private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
/** /**
* Sub-delims for use in a regex. * Sub-delims for use in a regex.
* *
* @link https://tools.ietf.org/html/rfc3986#section-2.2 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
*/ */
private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26']; private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
@ -87,6 +87,7 @@ class Uri implements UriInterface, \JsonSerializable
$this->applyParts($parts); $this->applyParts($parts);
} }
} }
/** /**
* UTF-8 aware \parse_url() replacement. * UTF-8 aware \parse_url() replacement.
* *
@ -161,7 +162,7 @@ class Uri implements UriInterface, \JsonSerializable
* `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
* that format). * that format).
* *
* @link https://tools.ietf.org/html/rfc3986#section-5.3 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
*/ */
public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
{ {
@ -218,7 +219,7 @@ class Uri implements UriInterface, \JsonSerializable
* @see Uri::isNetworkPathReference * @see Uri::isNetworkPathReference
* @see Uri::isAbsolutePathReference * @see Uri::isAbsolutePathReference
* @see Uri::isRelativePathReference * @see Uri::isRelativePathReference
* @link https://tools.ietf.org/html/rfc3986#section-4 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
*/ */
public static function isAbsolute(UriInterface $uri): bool public static function isAbsolute(UriInterface $uri): bool
{ {
@ -230,7 +231,7 @@ class Uri implements UriInterface, \JsonSerializable
* *
* A relative reference that begins with two slash characters is termed an network-path reference. * A relative reference that begins with two slash characters is termed an network-path reference.
* *
* @link https://tools.ietf.org/html/rfc3986#section-4.2 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
*/ */
public static function isNetworkPathReference(UriInterface $uri): bool public static function isNetworkPathReference(UriInterface $uri): bool
{ {
@ -242,7 +243,7 @@ class Uri implements UriInterface, \JsonSerializable
* *
* A relative reference that begins with a single slash character is termed an absolute-path reference. * A relative reference that begins with a single slash character is termed an absolute-path reference.
* *
* @link https://tools.ietf.org/html/rfc3986#section-4.2 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
*/ */
public static function isAbsolutePathReference(UriInterface $uri): bool public static function isAbsolutePathReference(UriInterface $uri): bool
{ {
@ -257,7 +258,7 @@ class Uri implements UriInterface, \JsonSerializable
* *
* A relative reference that does not begin with a slash character is termed a relative-path reference. * A relative reference that does not begin with a slash character is termed a relative-path reference.
* *
* @link https://tools.ietf.org/html/rfc3986#section-4.2 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
*/ */
public static function isRelativePathReference(UriInterface $uri): bool public static function isRelativePathReference(UriInterface $uri): bool
{ {
@ -276,7 +277,7 @@ class Uri implements UriInterface, \JsonSerializable
* @param UriInterface $uri The URI to check * @param UriInterface $uri The URI to check
* @param UriInterface|null $base An optional base URI to compare against * @param UriInterface|null $base An optional base URI to compare against
* *
* @link https://tools.ietf.org/html/rfc3986#section-4.4 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
*/ */
public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool
{ {
@ -336,7 +337,7 @@ class Uri implements UriInterface, \JsonSerializable
* It has the same behavior as withQueryValue() but for an associative array of key => value. * It has the same behavior as withQueryValue() but for an associative array of key => value.
* *
* @param UriInterface $uri URI to use as a base. * @param UriInterface $uri URI to use as a base.
* @param array<string, string|null> $keyValueArray Associative array of key and values * @param (string|null)[] $keyValueArray Associative array of key and values
*/ */
public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
{ {
@ -352,7 +353,7 @@ class Uri implements UriInterface, \JsonSerializable
/** /**
* Creates a URI from a hash of `parse_url` components. * Creates a URI from a hash of `parse_url` components.
* *
* @link http://php.net/manual/en/function.parse-url.php * @see https://www.php.net/manual/en/function.parse-url.php
* *
* @throws MalformedUriException If the components do not form a valid URI. * @throws MalformedUriException If the components do not form a valid URI.
*/ */
@ -627,7 +628,7 @@ class Uri implements UriInterface, \JsonSerializable
} }
$port = (int) $port; $port = (int) $port;
if (0 > $port || 0xffff < $port) { if (0 > $port || 0xFFFF < $port) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
sprintf('Invalid port: %d. Must be between 0 and 65535', $port) sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
); );
@ -637,7 +638,7 @@ class Uri implements UriInterface, \JsonSerializable
} }
/** /**
* @param string[] $keys * @param (string|int)[] $keys
* *
* @return string[] * @return string[]
*/ */
@ -649,7 +650,9 @@ class Uri implements UriInterface, \JsonSerializable
return []; return [];
} }
$decodedKeys = array_map('rawurldecode', $keys); $decodedKeys = array_map(function ($k): string {
return rawurldecode((string) $k);
}, $keys);
return array_filter(explode('&', $current), function ($part) use ($decodedKeys) { return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true); return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);

View File

@ -11,7 +11,7 @@ use Psr\Http\Message\UriInterface;
* *
* @author Tobias Schultze * @author Tobias Schultze
* *
* @link https://tools.ietf.org/html/rfc3986#section-6 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6
*/ */
final class UriNormalizer final class UriNormalizer
{ {
@ -119,7 +119,7 @@ final class UriNormalizer
* @param UriInterface $uri The URI to normalize * @param UriInterface $uri The URI to normalize
* @param int $flags A bitmask of normalizations to apply, see constants * @param int $flags A bitmask of normalizations to apply, see constants
* *
* @link https://tools.ietf.org/html/rfc3986#section-6.2 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
*/ */
public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
{ {
@ -131,8 +131,8 @@ final class UriNormalizer
$uri = self::decodeUnreservedCharacters($uri); $uri = self::decodeUnreservedCharacters($uri);
} }
if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' && if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === ''
($uri->getScheme() === 'http' || $uri->getScheme() === 'https') && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
) { ) {
$uri = $uri->withPath('/'); $uri = $uri->withPath('/');
} }
@ -174,7 +174,7 @@ final class UriNormalizer
* @param UriInterface $uri2 An URI to compare * @param UriInterface $uri2 An URI to compare
* @param int $normalizations A bitmask of normalizations to apply, see constants * @param int $normalizations A bitmask of normalizations to apply, see constants
* *
* @link https://tools.ietf.org/html/rfc3986#section-6.1 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1
*/ */
public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
{ {
@ -185,7 +185,7 @@ final class UriNormalizer
{ {
$regex = '/(?:%[A-Fa-f0-9]{2})++/'; $regex = '/(?:%[A-Fa-f0-9]{2})++/';
$callback = function (array $match) { $callback = function (array $match): string {
return strtoupper($match[0]); return strtoupper($match[0]);
}; };
@ -201,7 +201,7 @@ final class UriNormalizer
{ {
$regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
$callback = function (array $match) { $callback = function (array $match): string {
return rawurldecode($match[0]); return rawurldecode($match[0]);
}; };

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