updated plugin WP-WebAuthn version 1.3.4
This commit is contained in:
@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit09e765e3690d5165ed98a315471eec7d::getLoader();
|
||||
return ComposerAutoloaderInite99fdfd0dbb5e609b534e430fe6b54ef::getLoader();
|
||||
|
||||
@ -91,6 +91,7 @@ return array(
|
||||
'CBOR\\TextStringWithChunkObject' => $vendorDir . '/spomky-labs/cbor-php/src/TextStringWithChunkObject.php',
|
||||
'CBOR\\UnsignedIntegerObject' => $vendorDir . '/spomky-labs/cbor-php/src/UnsignedIntegerObject.php',
|
||||
'CBOR\\Utils' => $vendorDir . '/spomky-labs/cbor-php/src/Utils.php',
|
||||
'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Cose\\Algorithm\\Algorithm' => $vendorDir . '/web-auth/cose-lib/src/Algorithm/Algorithm.php',
|
||||
'Cose\\Algorithm\\Mac\\HS256' => $vendorDir . '/web-auth/cose-lib/src/Algorithm/Mac/HS256.php',
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit09e765e3690d5165ed98a315471eec7d
|
||||
class ComposerAutoloaderInite99fdfd0dbb5e609b534e430fe6b54ef
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
@ -22,16 +22,16 @@ class ComposerAutoloaderInit09e765e3690d5165ed98a315471eec7d
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit09e765e3690d5165ed98a315471eec7d', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInite99fdfd0dbb5e609b534e430fe6b54ef', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit09e765e3690d5165ed98a315471eec7d', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInite99fdfd0dbb5e609b534e430fe6b54ef', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit09e765e3690d5165ed98a315471eec7d::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit09e765e3690d5165ed98a315471eec7d::$files;
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit09e765e3690d5165ed98a315471eec7d
|
||||
class ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef
|
||||
{
|
||||
public static $files = array (
|
||||
'a4ecaeafb8cfb009ad0e052c90355e98' => __DIR__ . '/..' . '/beberlei/assert/lib/Assert/functions.php',
|
||||
@ -359,6 +359,7 @@ class ComposerStaticInit09e765e3690d5165ed98a315471eec7d
|
||||
'CBOR\\TextStringWithChunkObject' => __DIR__ . '/..' . '/spomky-labs/cbor-php/src/TextStringWithChunkObject.php',
|
||||
'CBOR\\UnsignedIntegerObject' => __DIR__ . '/..' . '/spomky-labs/cbor-php/src/UnsignedIntegerObject.php',
|
||||
'CBOR\\Utils' => __DIR__ . '/..' . '/spomky-labs/cbor-php/src/Utils.php',
|
||||
'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Cose\\Algorithm\\Algorithm' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Algorithm/Algorithm.php',
|
||||
'Cose\\Algorithm\\Mac\\HS256' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Algorithm/Mac/HS256.php',
|
||||
@ -939,9 +940,9 @@ class ComposerStaticInit09e765e3690d5165ed98a315471eec7d
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit09e765e3690d5165ed98a315471eec7d::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit09e765e3690d5165ed98a315471eec7d::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit09e765e3690d5165ed98a315471eec7d::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInite99fdfd0dbb5e609b534e430fe6b54ef::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
||||
@ -232,17 +232,17 @@
|
||||
},
|
||||
{
|
||||
"name": "league/uri",
|
||||
"version": "6.7.2",
|
||||
"version_normalized": "6.7.2.0",
|
||||
"version": "6.8.0",
|
||||
"version_normalized": "6.8.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/uri.git",
|
||||
"reference": "d3b50812dd51f3fbf176344cc2981db03d10fe06"
|
||||
"reference": "a700b4656e4c54371b799ac61e300ab25a2d1d39"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/uri/zipball/d3b50812dd51f3fbf176344cc2981db03d10fe06",
|
||||
"reference": "d3b50812dd51f3fbf176344cc2981db03d10fe06",
|
||||
"url": "https://api.github.com/repos/thephpleague/uri/zipball/a700b4656e4c54371b799ac61e300ab25a2d1d39",
|
||||
"reference": "a700b4656e4c54371b799ac61e300ab25a2d1d39",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -254,22 +254,23 @@
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"league/uri-interfaces": "^2.3",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/http-message": "^1.0"
|
||||
"php": "^8.1",
|
||||
"psr/http-message": "^1.0.1"
|
||||
},
|
||||
"conflict": {
|
||||
"league/uri-schemes": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^v3.3.2",
|
||||
"nyholm/psr7": "^1.5",
|
||||
"php-http/psr7-integration-tests": "^1.1",
|
||||
"phpstan/phpstan": "^1.2.0",
|
||||
"friendsofphp/php-cs-fixer": "^v3.9.5",
|
||||
"nyholm/psr7": "^1.5.1",
|
||||
"php-http/psr7-integration-tests": "^1.1.1",
|
||||
"phpbench/phpbench": "^1.2.6",
|
||||
"phpstan/phpstan": "^1.8.5",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.1.0",
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"psr/http-factory": "^1.0"
|
||||
"phpstan/phpstan-phpunit": "^1.1.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.4.3",
|
||||
"phpunit/phpunit": "^9.5.24",
|
||||
"psr/http-factory": "^1.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-fileinfo": "Needed to create Data URI from a filepath",
|
||||
@ -277,7 +278,7 @@
|
||||
"league/uri-components": "Needed to easily manipulate URI objects",
|
||||
"psr/http-factory": "Needed to use the URI factory"
|
||||
},
|
||||
"time": "2022-09-13T19:50:42+00:00",
|
||||
"time": "2022-09-13T19:58:47+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -328,7 +329,7 @@
|
||||
"docs": "https://uri.thephpleague.com",
|
||||
"forum": "https://thephpleague.slack.com",
|
||||
"issues": "https://github.com/thephpleague/uri/issues",
|
||||
"source": "https://github.com/thephpleague/uri/tree/6.7.2"
|
||||
"source": "https://github.com/thephpleague/uri/tree/6.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -420,17 +421,17 @@
|
||||
},
|
||||
{
|
||||
"name": "nyholm/psr7",
|
||||
"version": "1.8.0",
|
||||
"version_normalized": "1.8.0.0",
|
||||
"version": "1.8.2",
|
||||
"version_normalized": "1.8.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Nyholm/psr7.git",
|
||||
"reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be"
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Nyholm/psr7/zipball/3cb4d163b58589e47b35103e8e5e6a6a475b47be",
|
||||
"reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be",
|
||||
"url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -456,7 +457,7 @@
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"symfony/error-handler": "^4.4"
|
||||
},
|
||||
"time": "2023-05-02T11:26:24+00:00",
|
||||
"time": "2024-09-09T07:06:30+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -491,7 +492,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Nyholm/psr7/issues",
|
||||
"source": "https://github.com/Nyholm/psr7/tree/1.8.0"
|
||||
"source": "https://github.com/Nyholm/psr7/tree/1.8.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -582,17 +583,17 @@
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.1",
|
||||
"version_normalized": "1.0.1.0",
|
||||
"version": "1.0.3",
|
||||
"version_normalized": "1.0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
|
||||
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -603,9 +604,9 @@
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0"
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"time": "2020-06-29T06:28:15+00:00",
|
||||
"time": "2023-09-23T14:17:50+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -625,7 +626,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
@ -637,7 +638,7 @@
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client/tree/master"
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"install-path": "../psr/http-client"
|
||||
},
|
||||
@ -1197,17 +1198,17 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"version": "v1.31.0",
|
||||
"version_normalized": "1.31.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -1217,7 +1218,7 @@
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
@ -1225,12 +1226,9 @@
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"time": "2024-09-09T11:45:10+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
@ -1268,7 +1266,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1288,17 +1286,17 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"version": "v1.31.0",
|
||||
"version_normalized": "1.31.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -1308,14 +1306,11 @@
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"time": "2024-09-09T11:45:10+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
@ -1360,7 +1355,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1380,17 +1375,17 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"version": "v1.30.0",
|
||||
"version_normalized": "1.30.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
|
||||
"reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
|
||||
"reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -1402,12 +1397,9 @@
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"time": "2024-06-19T12:30:46+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
@ -1448,7 +1440,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1468,17 +1460,17 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.28",
|
||||
"version_normalized": "5.4.28.0",
|
||||
"version": "v5.4.40",
|
||||
"version_normalized": "5.4.40.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "45261e1fccad1b5447a8d7a8e67aa7b4a9798b7b"
|
||||
"reference": "deedcb3bb4669cae2148bc920eafd2b16dc7c046"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/45261e1fccad1b5447a8d7a8e67aa7b4a9798b7b",
|
||||
"reference": "45261e1fccad1b5447a8d7a8e67aa7b4a9798b7b",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/deedcb3bb4669cae2148bc920eafd2b16dc7c046",
|
||||
"reference": "deedcb3bb4669cae2148bc920eafd2b16dc7c046",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -1491,7 +1483,7 @@
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"time": "2023-08-07T10:36:04+00:00",
|
||||
"time": "2024-05-31T14:33:22+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
@ -1519,7 +1511,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.28"
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.40"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2009,6 +2001,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-core"
|
||||
},
|
||||
{
|
||||
@ -2095,6 +2088,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-key-mgmt"
|
||||
},
|
||||
{
|
||||
@ -2180,6 +2174,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-signature"
|
||||
},
|
||||
{
|
||||
@ -2258,6 +2253,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-signature-algorithm-ecdsa"
|
||||
},
|
||||
{
|
||||
@ -2336,6 +2332,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-signature-algorithm-eddsa"
|
||||
},
|
||||
{
|
||||
@ -2419,6 +2416,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "web-token/jwt-library",
|
||||
"install-path": "../web-token/jwt-signature-algorithm-rsa"
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.3.1',
|
||||
'version' => '1.3.1.0',
|
||||
'reference' => '7ba904e1020a15ea593301b05ecfc4d74aa61570',
|
||||
'pretty_version' => '1.3.4',
|
||||
'version' => '1.3.4.0',
|
||||
'reference' => '88207a7a7032f209d572a80f06699769886cdeb3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -11,9 +11,9 @@
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.3.1',
|
||||
'version' => '1.3.1.0',
|
||||
'reference' => '7ba904e1020a15ea593301b05ecfc4d74aa61570',
|
||||
'pretty_version' => '1.3.4',
|
||||
'version' => '1.3.4.0',
|
||||
'reference' => '88207a7a7032f209d572a80f06699769886cdeb3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -47,9 +47,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/uri' => array(
|
||||
'pretty_version' => '6.7.2',
|
||||
'version' => '6.7.2.0',
|
||||
'reference' => 'd3b50812dd51f3fbf176344cc2981db03d10fe06',
|
||||
'pretty_version' => '6.8.0',
|
||||
'version' => '6.8.0.0',
|
||||
'reference' => 'a700b4656e4c54371b799ac61e300ab25a2d1d39',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../league/uri',
|
||||
'aliases' => array(),
|
||||
@ -65,9 +65,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nyholm/psr7' => array(
|
||||
'pretty_version' => '1.8.0',
|
||||
'version' => '1.8.0.0',
|
||||
'reference' => '3cb4d163b58589e47b35103e8e5e6a6a475b47be',
|
||||
'pretty_version' => '1.8.2',
|
||||
'version' => '1.8.2.0',
|
||||
'reference' => 'a71f2b11690f4b24d099d6b16690a90ae14fc6f3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nyholm/psr7',
|
||||
'aliases' => array(),
|
||||
@ -89,9 +89,9 @@
|
||||
),
|
||||
),
|
||||
'psr/http-client' => array(
|
||||
'pretty_version' => '1.0.1',
|
||||
'version' => '1.0.1.0',
|
||||
'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
|
||||
'pretty_version' => '1.0.3',
|
||||
'version' => '1.0.3.0',
|
||||
'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-client',
|
||||
'aliases' => array(),
|
||||
@ -179,36 +179,36 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-ctype' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php81' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '707403074c8ea6e2edaf8794b0157a0bfa52157a',
|
||||
'pretty_version' => 'v1.30.0',
|
||||
'version' => '1.30.0.0',
|
||||
'reference' => '3fb075789fb91f9ad9af537c4012d523085bd5af',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php81',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/process' => array(
|
||||
'pretty_version' => 'v5.4.28',
|
||||
'version' => '5.4.28.0',
|
||||
'reference' => '45261e1fccad1b5447a8d7a8e67aa7b4a9798b7b',
|
||||
'pretty_version' => 'v5.4.40',
|
||||
'version' => '5.4.40.0',
|
||||
'reference' => 'deedcb3bb4669cae2148bc920eafd2b16dc7c046',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/process',
|
||||
'aliases' => array(),
|
||||
|
||||
@ -44,21 +44,22 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php": "^8.1",
|
||||
"ext-json": "*",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-message": "^1.0.1",
|
||||
"league/uri-interfaces": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^v3.3.2",
|
||||
"nyholm/psr7": "^1.5",
|
||||
"php-http/psr7-integration-tests": "^1.1",
|
||||
"phpstan/phpstan": "^1.2.0",
|
||||
"friendsofphp/php-cs-fixer": "^v3.9.5",
|
||||
"nyholm/psr7": "^1.5.1",
|
||||
"php-http/psr7-integration-tests": "^1.1.1",
|
||||
"phpbench/phpbench": "^1.2.6",
|
||||
"phpstan/phpstan": "^1.8.5",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.1.0",
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"psr/http-factory": "^1.0"
|
||||
"phpstan/phpstan-phpunit": "^1.1.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.4.3",
|
||||
"phpunit/phpunit": "^9.5.24",
|
||||
"psr/http-factory": "^1.0.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -74,6 +75,7 @@
|
||||
"league/uri-schemes": "^1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "phpbench run src --report=default",
|
||||
"phpcs": "php-cs-fixer fix -v --diff --dry-run --allow-risky=yes --ansi",
|
||||
"phpcs:fix": "php-cs-fixer fix -vvv --allow-risky=yes --ansi",
|
||||
"phpstan": "phpstan analyse -l max -c phpstan.neon src --ansi --memory-limit=256M",
|
||||
|
||||
@ -17,19 +17,14 @@ use JsonSerializable;
|
||||
use League\Uri\Contracts\UriInterface;
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use Psr\Http\Message\UriInterface as Psr7UriInterface;
|
||||
use function is_object;
|
||||
use Stringable;
|
||||
use function is_scalar;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
|
||||
final class Http implements Psr7UriInterface, JsonSerializable
|
||||
{
|
||||
private UriInterface $uri;
|
||||
|
||||
private function __construct(UriInterface $uri)
|
||||
private function __construct(private readonly UriInterface $uri)
|
||||
{
|
||||
$this->validate($uri);
|
||||
$this->uri = $uri;
|
||||
$this->validate($this->uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,19 +34,18 @@ final class Http implements Psr7UriInterface, JsonSerializable
|
||||
*/
|
||||
private function validate(UriInterface $uri): void
|
||||
{
|
||||
$scheme = $uri->getScheme();
|
||||
if (null === $scheme && '' === $uri->getHost()) {
|
||||
throw new SyntaxError(sprintf('an URI without scheme can not contains a empty host string according to PSR-7: %s', (string) $uri));
|
||||
if (null === $uri->getScheme() && '' === $uri->getHost()) {
|
||||
throw new SyntaxError('An URI without scheme can not contains a empty host string according to PSR-7: '.$uri);
|
||||
}
|
||||
|
||||
$port = $uri->getPort();
|
||||
if (null !== $port && ($port < 0 || $port > 65535)) {
|
||||
throw new SyntaxError(sprintf('The URI port is outside the established TCP and UDP port ranges: %s', (string) $uri->getPort()));
|
||||
throw new SyntaxError('The URI port is outside the established TCP and UDP port ranges: '.$uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method called by PHP's var export.
|
||||
* @param array{uri:UriInterface} $components
|
||||
*/
|
||||
public static function __set_state(array $components): self
|
||||
{
|
||||
@ -60,10 +54,8 @@ final class Http implements Psr7UriInterface, JsonSerializable
|
||||
|
||||
/**
|
||||
* Create a new instance from a string.
|
||||
*
|
||||
* @param string|mixed $uri
|
||||
*/
|
||||
public static function createFromString($uri = ''): self
|
||||
public static function createFromString(Stringable|UriInterface|String $uri = ''): self
|
||||
{
|
||||
return new self(Uri::createFromString($uri));
|
||||
}
|
||||
@ -91,21 +83,18 @@ final class Http implements Psr7UriInterface, JsonSerializable
|
||||
* Create a new instance from a URI and a Base URI.
|
||||
*
|
||||
* The returned URI must be absolute.
|
||||
*
|
||||
* @param mixed $uri the input URI to create
|
||||
* @param mixed $base_uri the base URI used for reference
|
||||
*/
|
||||
public static function createFromBaseUri($uri, $base_uri = null): self
|
||||
{
|
||||
public static function createFromBaseUri(
|
||||
Stringable|UriInterface|String $uri,
|
||||
Stringable|UriInterface|String $base_uri = null
|
||||
): self {
|
||||
return new self(Uri::createFromBaseUri($uri, $base_uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance from a URI object.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri the input URI to create
|
||||
*/
|
||||
public static function createFromUri($uri): self
|
||||
public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
|
||||
{
|
||||
if ($uri instanceof UriInterface) {
|
||||
return new self($uri);
|
||||
@ -178,145 +167,6 @@ final class Http implements Psr7UriInterface, JsonSerializable
|
||||
return (string) $this->uri->getFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withScheme($scheme): self
|
||||
{
|
||||
/** @var string $scheme */
|
||||
$scheme = $this->filterInput($scheme);
|
||||
if ('' === $scheme) {
|
||||
$scheme = null;
|
||||
}
|
||||
|
||||
$uri = $this->uri->withScheme($scheme);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely stringify input when possible.
|
||||
*
|
||||
* @param mixed $str the value to evaluate as a string
|
||||
*
|
||||
* @throws SyntaxError if the submitted data can not be converted to string
|
||||
*
|
||||
* @return string|mixed
|
||||
*/
|
||||
private function filterInput($str)
|
||||
{
|
||||
if (is_scalar($str) || (is_object($str) && method_exists($str, '__toString'))) {
|
||||
return (string) $str;
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withUserInfo($user, $password = null): self
|
||||
{
|
||||
/** @var string $user */
|
||||
$user = $this->filterInput($user);
|
||||
if ('' === $user) {
|
||||
$user = null;
|
||||
}
|
||||
|
||||
$uri = $this->uri->withUserInfo($user, $password);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withHost($host): self
|
||||
{
|
||||
/** @var string $host */
|
||||
$host = $this->filterInput($host);
|
||||
if ('' === $host) {
|
||||
$host = null;
|
||||
}
|
||||
|
||||
$uri = $this->uri->withHost($host);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withPort($port): self
|
||||
{
|
||||
$uri = $this->uri->withPort($port);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withPath($path): self
|
||||
{
|
||||
$uri = $this->uri->withPath($path);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withQuery($query): self
|
||||
{
|
||||
/** @var string $query */
|
||||
$query = $this->filterInput($query);
|
||||
if ('' === $query) {
|
||||
$query = null;
|
||||
}
|
||||
|
||||
$uri = $this->uri->withQuery($query);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withFragment($fragment): self
|
||||
{
|
||||
/** @var string $fragment */
|
||||
$fragment = $this->filterInput($fragment);
|
||||
if ('' === $fragment) {
|
||||
$fragment = null;
|
||||
}
|
||||
|
||||
$uri = $this->uri->withFragment($fragment);
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -332,4 +182,88 @@ final class Http implements Psr7UriInterface, JsonSerializable
|
||||
{
|
||||
return $this->uri->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely stringify input when possible for League UriInterface compatibility.
|
||||
*
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
private function filterInput(mixed $str): string|null
|
||||
{
|
||||
if (!is_scalar($str) && !$str instanceof Stringable) {
|
||||
throw new SyntaxError('The component must be a string, a scalar or a Stringable object; `'.gettype($str).'` given.');
|
||||
}
|
||||
|
||||
$str = (string) $str;
|
||||
if ('' === $str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function newInstance(UriInterface $uri): self
|
||||
{
|
||||
if ((string) $uri === (string) $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withScheme($scheme): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withScheme($this->filterInput($scheme)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withUserInfo($user, $password = null): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withUserInfo($this->filterInput($user), $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withHost($host): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withHost($this->filterInput($host)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withPort($port): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withPort($port));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withPath($path): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withPath($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withQuery($query): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withQuery($this->filterInput($query)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withFragment($fragment): self
|
||||
{
|
||||
return $this->newInstance($this->uri->withFragment($this->filterInput($fragment)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +21,11 @@ use League\Uri\Exceptions\IdnSupportMissing;
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\Idna\Idna;
|
||||
use Psr\Http\Message\UriInterface as Psr7UriInterface;
|
||||
use SensitiveParameter;
|
||||
use Stringable;
|
||||
use TypeError;
|
||||
use function array_filter;
|
||||
use function array_key_first;
|
||||
use function array_map;
|
||||
use function base64_decode;
|
||||
use function base64_encode;
|
||||
@ -33,14 +36,15 @@ use function filter_var;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function inet_pton;
|
||||
use function is_object;
|
||||
use function is_int;
|
||||
use function is_scalar;
|
||||
use function method_exists;
|
||||
use function is_string;
|
||||
use function ltrim;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function preg_replace_callback;
|
||||
use function rawurlencode;
|
||||
use function sprintf;
|
||||
use function str_contains;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
@ -81,14 +85,14 @@ final class Uri implements UriInterface
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const REGEXP_CHARS_UNRESERVED = 'A-Za-z0-9_\-\.~';
|
||||
private const REGEXP_CHARS_UNRESERVED = 'A-Za-z\d_\-\.~';
|
||||
|
||||
/**
|
||||
* RFC3986 schema regular expression pattern.
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
*/
|
||||
private const REGEXP_SCHEME = ',^[a-z]([-a-z0-9+.]+)?$,i';
|
||||
private const REGEXP_SCHEME = ',^[a-z]([-a-z\d+.]+)?$,i';
|
||||
|
||||
/**
|
||||
* RFC3986 host identified by a registered name regular expression pattern.
|
||||
@ -96,9 +100,9 @@ final class Uri implements UriInterface
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-3.2.2
|
||||
*/
|
||||
private const REGEXP_HOST_REGNAME = '/^(
|
||||
(?<unreserved>[a-z0-9_~\-\.])|
|
||||
(?<unreserved>[a-z\d_~\-\.])|
|
||||
(?<sub_delims>[!$&\'()*+,;=])|
|
||||
(?<encoded>%[A-F0-9]{2})
|
||||
(?<encoded>%[A-F\d]{2})
|
||||
)+$/x';
|
||||
|
||||
/**
|
||||
@ -114,9 +118,9 @@ final class Uri implements UriInterface
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-3.2.2
|
||||
*/
|
||||
private const REGEXP_HOST_IPFUTURE = '/^
|
||||
v(?<version>[A-F0-9])+\.
|
||||
v(?<version>[A-F\d])+\.
|
||||
(?:
|
||||
(?<unreserved>[a-z0-9_~\-\.])|
|
||||
(?<unreserved>[a-z\d_~\-\.])|
|
||||
(?<sub_delims>[!$&\'()*+,;=:]) # also include the : character
|
||||
)+
|
||||
$/ix';
|
||||
@ -176,20 +180,11 @@ final class Uri implements UriInterface
|
||||
];
|
||||
|
||||
/**
|
||||
* URI validation methods per scheme.
|
||||
* Maximum number of formatted host cached.
|
||||
*
|
||||
* @var array<string>
|
||||
* @var int
|
||||
*/
|
||||
private const SCHEME_VALIDATION_METHOD = [
|
||||
'data' => 'isUriWithSchemeAndPathOnly',
|
||||
'file' => 'isUriWithSchemeHostAndPathOnly',
|
||||
'ftp' => 'isNonEmptyHostUriWithoutFragmentAndQuery',
|
||||
'gopher' => 'isNonEmptyHostUriWithoutFragmentAndQuery',
|
||||
'http' => 'isNonEmptyHostUri',
|
||||
'https' => 'isNonEmptyHostUri',
|
||||
'ws' => 'isNonEmptyHostUriWithoutFragment',
|
||||
'wss' => 'isNonEmptyHostUriWithoutFragment',
|
||||
];
|
||||
private const MAXIMUM_FORMATTED_HOST_CACHED = 100;
|
||||
|
||||
/**
|
||||
* All ASCII letters sorted by typical frequency of occurrence.
|
||||
@ -203,7 +198,7 @@ final class Uri implements UriInterface
|
||||
private ?string $host;
|
||||
private ?int $port;
|
||||
private ?string $authority;
|
||||
private string $path = '';
|
||||
private string $path;
|
||||
private ?string $query;
|
||||
private ?string $fragment;
|
||||
private ?string $uri;
|
||||
@ -211,7 +206,7 @@ final class Uri implements UriInterface
|
||||
private function __construct(
|
||||
?string $scheme,
|
||||
?string $user,
|
||||
?string $pass,
|
||||
#[SensitiveParameter] ?string $pass,
|
||||
?string $host,
|
||||
?int $port,
|
||||
string $path,
|
||||
@ -226,49 +221,49 @@ final class Uri implements UriInterface
|
||||
$this->path = $this->formatPath($path);
|
||||
$this->query = $this->formatQueryAndFragment($query);
|
||||
$this->fragment = $this->formatQueryAndFragment($fragment);
|
||||
|
||||
$this->assertValidState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the Scheme and Host component.
|
||||
*
|
||||
* @param ?string $scheme
|
||||
* @throws SyntaxError if the scheme is invalid
|
||||
*/
|
||||
private function formatScheme(?string $scheme): ?string
|
||||
{
|
||||
if (null === $scheme) {
|
||||
if (null === $scheme || array_key_exists($scheme, self::SCHEME_DEFAULT_PORT)) {
|
||||
return $scheme;
|
||||
}
|
||||
|
||||
$formatted_scheme = strtolower($scheme);
|
||||
if (1 === preg_match(self::REGEXP_SCHEME, $formatted_scheme)) {
|
||||
if (array_key_exists($formatted_scheme, self::SCHEME_DEFAULT_PORT) || 1 === preg_match(self::REGEXP_SCHEME, $formatted_scheme)) {
|
||||
return $formatted_scheme;
|
||||
}
|
||||
|
||||
throw new SyntaxError(sprintf('The scheme `%s` is invalid.', $scheme));
|
||||
throw new SyntaxError('The scheme `'.$scheme.'` is invalid.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the UserInfo component.
|
||||
* @param ?string $user
|
||||
* @param ?string $password
|
||||
*/
|
||||
private function formatUserInfo(?string $user, ?string $password): ?string
|
||||
{
|
||||
private function formatUserInfo(
|
||||
?string $user,
|
||||
#[SensitiveParameter] ?string $password
|
||||
): ?string {
|
||||
if (null === $user) {
|
||||
return $user;
|
||||
return null;
|
||||
}
|
||||
|
||||
static $user_pattern = '/(?:[^%'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.']++|%(?![A-Fa-f0-9]{2}))/';
|
||||
$user = preg_replace_callback($user_pattern, [Uri::class, 'urlEncodeMatch'], $user);
|
||||
static $user_pattern = '/[^%'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.']++|%(?![A-Fa-f\d]{2})/';
|
||||
$user = preg_replace_callback($user_pattern, Uri::urlEncodeMatch(...), $user);
|
||||
if (null === $password) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
static $password_pattern = '/(?:[^%:'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.']++|%(?![A-Fa-f0-9]{2}))/';
|
||||
static $password_pattern = '/[^%:'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.']++|%(?![A-Fa-f\d]{2})/';
|
||||
|
||||
return $user.':'.preg_replace_callback($password_pattern, [Uri::class, 'urlEncodeMatch'], $password);
|
||||
return $user.':'.preg_replace_callback($password_pattern, Uri::urlEncodeMatch(...), $password);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +276,6 @@ final class Uri implements UriInterface
|
||||
|
||||
/**
|
||||
* Validate and Format the Host component.
|
||||
* @param ?string $host
|
||||
*/
|
||||
private function formatHost(?string $host): ?string
|
||||
{
|
||||
@ -289,11 +283,18 @@ final class Uri implements UriInterface
|
||||
return $host;
|
||||
}
|
||||
|
||||
if ('[' !== $host[0]) {
|
||||
return $this->formatRegisteredName($host);
|
||||
static $formattedHostCache = [];
|
||||
if (isset($formattedHostCache[$host])) {
|
||||
return $formattedHostCache[$host];
|
||||
}
|
||||
|
||||
return $this->formatIp($host);
|
||||
$formattedHost = '[' === $host[0] ? $this->formatIp($host) : $this->formatRegisteredName($host);
|
||||
$formattedHostCache[$host] = $formattedHost;
|
||||
if (self::MAXIMUM_FORMATTED_HOST_CACHED < count($formattedHostCache)) {
|
||||
unset($formattedHostCache[array_key_first($formattedHostCache)]);
|
||||
}
|
||||
|
||||
return $formattedHost;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,7 +313,7 @@ final class Uri implements UriInterface
|
||||
}
|
||||
|
||||
if (1 === preg_match(self::REGEXP_HOST_GEN_DELIMS, $formatted_host)) {
|
||||
throw new SyntaxError(sprintf('The host `%s` is invalid : a registered name can not contain URI delimiters or spaces', $host));
|
||||
throw new SyntaxError('The host `'.$host.'` is invalid : a registered name can not contain URI delimiters or spaces.');
|
||||
}
|
||||
|
||||
$info = Idna::toAscii($host, Idna::IDNA2008_ASCII);
|
||||
@ -341,35 +342,33 @@ final class Uri implements UriInterface
|
||||
|
||||
$pos = strpos($ip, '%');
|
||||
if (false === $pos) {
|
||||
throw new SyntaxError(sprintf('The host `%s` is invalid : the IP host is malformed', $host));
|
||||
throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
|
||||
}
|
||||
|
||||
if (1 === preg_match(self::REGEXP_HOST_GEN_DELIMS, rawurldecode(substr($ip, $pos)))) {
|
||||
throw new SyntaxError(sprintf('The host `%s` is invalid : the IP host is malformed', $host));
|
||||
throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
|
||||
}
|
||||
|
||||
$ip = substr($ip, 0, $pos);
|
||||
if (false === filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
throw new SyntaxError(sprintf('The host `%s` is invalid : the IP host is malformed', $host));
|
||||
throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
|
||||
}
|
||||
|
||||
//Only the address block fe80::/10 can have a Zone ID attach to
|
||||
//let's detect the link local significant 10 bits
|
||||
if (0 === strpos((string) inet_pton($ip), self::HOST_ADDRESS_BLOCK)) {
|
||||
if (str_starts_with((string)inet_pton($ip), self::HOST_ADDRESS_BLOCK)) {
|
||||
return $host;
|
||||
}
|
||||
|
||||
throw new SyntaxError(sprintf('The host `%s` is invalid : the IP host is malformed', $host));
|
||||
throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the Port component.
|
||||
*
|
||||
* @param object|null|int|string $port
|
||||
*
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
private function formatPort($port = null): ?int
|
||||
private function formatPort(Stringable|string|int|null $port = null): ?int
|
||||
{
|
||||
if (null === $port || '' === $port) {
|
||||
return null;
|
||||
@ -381,7 +380,7 @@ final class Uri implements UriInterface
|
||||
|
||||
$port = (int) $port;
|
||||
if (0 > $port) {
|
||||
throw new SyntaxError(sprintf('The port `%s` is invalid', $port));
|
||||
throw new SyntaxError('The port `'.$port.'` is invalid.');
|
||||
}
|
||||
|
||||
$defaultPort = self::SCHEME_DEFAULT_PORT[$this->scheme] ?? null;
|
||||
@ -392,9 +391,6 @@ final class Uri implements UriInterface
|
||||
return $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function __set_state(array $components): self
|
||||
{
|
||||
$components['user'] = null;
|
||||
@ -416,22 +412,21 @@ final class Uri implements UriInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance from a URI and a Base URI.
|
||||
* Creates a new instance from a URI and a Base URI.
|
||||
*
|
||||
* The returned URI must be absolute.
|
||||
*
|
||||
* @param mixed $uri the input URI to create
|
||||
* @param null|mixed $base_uri the base URI used for reference
|
||||
*/
|
||||
public static function createFromBaseUri($uri, $base_uri = null): UriInterface
|
||||
{
|
||||
public static function createFromBaseUri(
|
||||
Stringable|UriInterface|String $uri,
|
||||
Stringable|UriInterface|String $base_uri = null
|
||||
): UriInterface {
|
||||
if (!$uri instanceof UriInterface) {
|
||||
$uri = self::createFromString($uri);
|
||||
}
|
||||
|
||||
if (null === $base_uri) {
|
||||
if (null === $uri->getScheme()) {
|
||||
throw new SyntaxError(sprintf('the URI `%s` must be absolute', (string) $uri));
|
||||
throw new SyntaxError('the URI `'.$uri.'` must be absolute.');
|
||||
}
|
||||
|
||||
if (null === $uri->getAuthority()) {
|
||||
@ -449,7 +444,7 @@ final class Uri implements UriInterface
|
||||
}
|
||||
|
||||
if (null === $base_uri->getScheme()) {
|
||||
throw new SyntaxError(sprintf('the base URI `%s` must be absolute', (string) $base_uri));
|
||||
throw new SyntaxError('the base URI `'.$base_uri.'` must be absolute.');
|
||||
}
|
||||
|
||||
/** @var UriInterface $uri */
|
||||
@ -460,10 +455,8 @@ final class Uri implements UriInterface
|
||||
|
||||
/**
|
||||
* Create a new instance from a string.
|
||||
*
|
||||
* @param string|mixed $uri
|
||||
*/
|
||||
public static function createFromString($uri = ''): self
|
||||
public static function createFromString(Stringable|string $uri = ''): self
|
||||
{
|
||||
$components = UriString::parse($uri);
|
||||
|
||||
@ -517,7 +510,7 @@ final class Uri implements UriInterface
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$finfo_support) {
|
||||
throw new FileinfoSupportMissing(sprintf('Please install ext/fileinfo to use the %s() method.', __METHOD__));
|
||||
throw new FileinfoSupportMissing('Please install ext/fileinfo to use the '.__METHOD__.'() method.');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
@ -528,9 +521,12 @@ final class Uri implements UriInterface
|
||||
$mime_args[] = $context;
|
||||
}
|
||||
|
||||
$raw = @file_get_contents(...$file_args);
|
||||
set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);
|
||||
$raw = file_get_contents(...$file_args);
|
||||
restore_error_handler();
|
||||
|
||||
if (false === $raw) {
|
||||
throw new SyntaxError(sprintf('The file `%s` does not exist or is not readable', $path));
|
||||
throw new SyntaxError('The file `'.$path.'` does not exist or is not readable.');
|
||||
}
|
||||
|
||||
$mimetype = (string) (new finfo(FILEINFO_MIME))->file(...$mime_args);
|
||||
@ -546,7 +542,7 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
public static function createFromUnixPath(string $uri = ''): self
|
||||
{
|
||||
$uri = implode('/', array_map('rawurlencode', explode('/', $uri)));
|
||||
$uri = implode('/', array_map(rawurlencode(...), explode('/', $uri)));
|
||||
if ('/' !== ($uri[0] ?? '')) {
|
||||
return Uri::createFromComponents(['path' => $uri]);
|
||||
}
|
||||
@ -565,7 +561,7 @@ final class Uri implements UriInterface
|
||||
$uri = substr($uri, strlen($root));
|
||||
}
|
||||
$uri = str_replace('\\', '/', $uri);
|
||||
$uri = implode('/', array_map('rawurlencode', explode('/', $uri)));
|
||||
$uri = implode('/', array_map(rawurlencode(...), explode('/', $uri)));
|
||||
|
||||
//Local Windows absolute path
|
||||
if ('' !== $root) {
|
||||
@ -573,7 +569,7 @@ final class Uri implements UriInterface
|
||||
}
|
||||
|
||||
//UNC Windows Path
|
||||
if ('//' !== substr($uri, 0, 2)) {
|
||||
if (!str_starts_with($uri, '//')) {
|
||||
return Uri::createFromComponents(['path' => $uri]);
|
||||
}
|
||||
|
||||
@ -584,10 +580,8 @@ final class Uri implements UriInterface
|
||||
|
||||
/**
|
||||
* Create a new instance from a URI object.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri the input URI to create
|
||||
*/
|
||||
public static function createFromUri($uri): self
|
||||
public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
|
||||
{
|
||||
if ($uri instanceof UriInterface) {
|
||||
$user_info = $uri->getUserInfo();
|
||||
@ -609,10 +603,6 @@ final class Uri implements UriInterface
|
||||
);
|
||||
}
|
||||
|
||||
if (!$uri instanceof Psr7UriInterface) {
|
||||
throw new TypeError(sprintf('The object must implement the `%s` or the `%s`', Psr7UriInterface::class, UriInterface::class));
|
||||
}
|
||||
|
||||
$scheme = $uri->getScheme();
|
||||
if ('' === $scheme) {
|
||||
$scheme = null;
|
||||
@ -657,19 +647,12 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
public static function createFromServer(array $server): self
|
||||
{
|
||||
[$user, $pass] = self::fetchUserInfo($server);
|
||||
[$host, $port] = self::fetchHostname($server);
|
||||
[$path, $query] = self::fetchRequestUri($server);
|
||||
$components = ['scheme' => self::fetchScheme($server)];
|
||||
[$components['user'], $components['pass']] = self::fetchUserInfo($server);
|
||||
[$components['host'], $components['port']] = self::fetchHostname($server);
|
||||
[$components['path'], $components['query']] = self::fetchRequestUri($server);
|
||||
|
||||
return Uri::createFromComponents([
|
||||
'scheme' => self::fetchScheme($server),
|
||||
'user' => $user,
|
||||
'pass' => $pass,
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'path' => $path,
|
||||
'query' => $query,
|
||||
]);
|
||||
return Uri::createFromComponents($components);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -686,14 +669,14 @@ final class Uri implements UriInterface
|
||||
/**
|
||||
* Returns the environment user info.
|
||||
*
|
||||
* @return array{0:?string, 1:?string}
|
||||
* @return non-empty-array{0:?string, 1:?string}
|
||||
*/
|
||||
private static function fetchUserInfo(array $server): array
|
||||
{
|
||||
$server += ['PHP_AUTH_USER' => null, 'PHP_AUTH_PW' => null, 'HTTP_AUTHORIZATION' => ''];
|
||||
$user = $server['PHP_AUTH_USER'];
|
||||
$pass = $server['PHP_AUTH_PW'];
|
||||
if (0 === strpos(strtolower($server['HTTP_AUTHORIZATION']), 'basic')) {
|
||||
if (str_starts_with(strtolower($server['HTTP_AUTHORIZATION']), 'basic')) {
|
||||
$userinfo = base64_decode(substr($server['HTTP_AUTHORIZATION'], 6), true);
|
||||
if (false === $userinfo) {
|
||||
throw new SyntaxError('The user info could not be detected');
|
||||
@ -751,20 +734,17 @@ final class Uri implements UriInterface
|
||||
/**
|
||||
* Returns the environment path.
|
||||
*
|
||||
* @return array{0:?string, 1:?string}
|
||||
* @return non-empty-array{0:?string, 1:?string}
|
||||
*/
|
||||
private static function fetchRequestUri(array $server): array
|
||||
{
|
||||
$server += ['IIS_WasUrlRewritten' => null, 'UNENCODED_URL' => '', 'PHP_SELF' => '', 'QUERY_STRING' => null];
|
||||
if ('1' === $server['IIS_WasUrlRewritten'] && '' !== $server['UNENCODED_URL']) {
|
||||
/** @var array{0:?string, 1:?string} $retval */
|
||||
$retval = explode('?', $server['UNENCODED_URL'], 2) + [1 => null];
|
||||
|
||||
return $retval;
|
||||
return explode('?', $server['UNENCODED_URL'], 2) + [1 => null];
|
||||
}
|
||||
|
||||
if (isset($server['REQUEST_URI'])) {
|
||||
[$path, ] = explode('?', $server['REQUEST_URI'], 2);
|
||||
[$path] = explode('?', $server['REQUEST_URI'], 2);
|
||||
$query = ('' !== $server['QUERY_STRING']) ? $server['QUERY_STRING'] : null;
|
||||
|
||||
return [$path, $query];
|
||||
@ -799,13 +779,21 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
private function formatPath(string $path): string
|
||||
{
|
||||
$path = $this->formatDataPath($path);
|
||||
if ('data' === $this->scheme) {
|
||||
$path = $this->formatDataPath($path);
|
||||
}
|
||||
|
||||
static $pattern = '/(?:[^'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.'%:@\/}{]++|%(?![A-Fa-f0-9]{2}))/';
|
||||
if ('/' !== $path) {
|
||||
static $pattern = '/[^'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.':@\/}{]++|%(?![A-Fa-f\d]{2})/';
|
||||
|
||||
$path = (string) preg_replace_callback($pattern, [Uri::class, 'urlEncodeMatch'], $path);
|
||||
$path = (string) preg_replace_callback($pattern, Uri::urlEncodeMatch(...), $path);
|
||||
}
|
||||
|
||||
return $this->formatFilePath($path);
|
||||
if ('file' === $this->scheme) {
|
||||
$path = $this->formatFilePath($path);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -817,16 +805,12 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
private function formatDataPath(string $path): string
|
||||
{
|
||||
if ('data' !== $this->scheme) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if ('' == $path) {
|
||||
return 'text/plain;charset=us-ascii,';
|
||||
}
|
||||
|
||||
if (strlen($path) !== strspn($path, self::ASCII) || false === strpos($path, ',')) {
|
||||
throw new SyntaxError(sprintf('The path `%s` is invalid according to RFC2937', $path));
|
||||
if (strlen($path) !== strspn($path, self::ASCII) || !str_contains($path, ',')) {
|
||||
throw new SyntaxError('The path `'.$path.'` is invalid according to RFC2937.');
|
||||
}
|
||||
|
||||
$parts = explode(',', $path, 2) + [1 => null];
|
||||
@ -857,7 +841,7 @@ final class Uri implements UriInterface
|
||||
private function assertValidPath(string $mimetype, string $parameters, string $data): void
|
||||
{
|
||||
if (1 !== preg_match(self::REGEXP_MIMETYPE, $mimetype)) {
|
||||
throw new SyntaxError(sprintf('The path mimetype `%s` is invalid', $mimetype));
|
||||
throw new SyntaxError('The path mimetype `'.$mimetype.'` is invalid.');
|
||||
}
|
||||
|
||||
$is_binary = 1 === preg_match(self::REGEXP_BINARY, $parameters, $matches);
|
||||
@ -865,9 +849,9 @@ final class Uri implements UriInterface
|
||||
$parameters = substr($parameters, 0, - strlen($matches[0]));
|
||||
}
|
||||
|
||||
$res = array_filter(array_filter(explode(';', $parameters), [$this, 'validateParameter']));
|
||||
$res = array_filter(array_filter(explode(';', $parameters), $this->validateParameter(...)));
|
||||
if ([] !== $res) {
|
||||
throw new SyntaxError(sprintf('The path paremeters `%s` is invalid', $parameters));
|
||||
throw new SyntaxError('The path paremeters `'.$parameters.'` is invalid.');
|
||||
}
|
||||
|
||||
if (!$is_binary) {
|
||||
@ -876,7 +860,7 @@ final class Uri implements UriInterface
|
||||
|
||||
$res = base64_decode($data, true);
|
||||
if (false === $res || $data !== base64_encode($res)) {
|
||||
throw new SyntaxError(sprintf('The path data `%s` is invalid', $data));
|
||||
throw new SyntaxError('The path data `'.$data.'` is invalid.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,10 +879,6 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
private function formatFilePath(string $path): string
|
||||
{
|
||||
if ('file' !== $this->scheme) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$replace = static function (array $matches): string {
|
||||
return $matches['delim'].$matches['volume'].':'.$matches['rest'];
|
||||
};
|
||||
@ -915,8 +895,6 @@ final class Uri implements UriInterface
|
||||
* <li> a boolean flag telling wether the delimiter is to be added to the component
|
||||
* when building the URI string representation</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ?string $component
|
||||
*/
|
||||
private function formatQueryAndFragment(?string $component): ?string
|
||||
{
|
||||
@ -924,8 +902,8 @@ final class Uri implements UriInterface
|
||||
return $component;
|
||||
}
|
||||
|
||||
static $pattern = '/(?:[^'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/';
|
||||
return preg_replace_callback($pattern, [Uri::class, 'urlEncodeMatch'], $component);
|
||||
static $pattern = '/[^'.self::REGEXP_CHARS_UNRESERVED.self::REGEXP_CHARS_SUBDELIM.':@\/?]++|%(?![A-Fa-f\d]{2})/';
|
||||
return preg_replace_callback($pattern, Uri::urlEncodeMatch(...), $component);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -943,27 +921,31 @@ final class Uri implements UriInterface
|
||||
throw new SyntaxError('If an authority is present the path must be empty or start with a `/`.');
|
||||
}
|
||||
|
||||
if (null === $this->authority && 0 === strpos($this->path, '//')) {
|
||||
throw new SyntaxError(sprintf('If there is no authority the path `%s` can not start with a `//`.', $this->path));
|
||||
if (null === $this->authority && str_starts_with($this->path, '//')) {
|
||||
throw new SyntaxError('If there is no authority the path `'.$this->path.'` can not start with a `//`.');
|
||||
}
|
||||
|
||||
$pos = strpos($this->path, ':');
|
||||
if (null === $this->authority
|
||||
&& null === $this->scheme
|
||||
&& false !== $pos
|
||||
&& false === strpos(substr($this->path, 0, $pos), '/')
|
||||
&& !str_contains(substr($this->path, 0, $pos), '/')
|
||||
) {
|
||||
throw new SyntaxError('In absence of a scheme and an authority the first path segment cannot contain a colon (":") character.');
|
||||
}
|
||||
|
||||
$validationMethod = self::SCHEME_VALIDATION_METHOD[$this->scheme] ?? null;
|
||||
if (null === $validationMethod || true === $this->$validationMethod()) {
|
||||
$this->uri = null;
|
||||
$this->uri = null;
|
||||
|
||||
return;
|
||||
if (! match ($this->scheme) {
|
||||
'data' => $this->isUriWithSchemeAndPathOnly(),
|
||||
'file' => $this->isUriWithSchemeHostAndPathOnly(),
|
||||
'ftp', 'gopher' => $this->isNonEmptyHostUriWithoutFragmentAndQuery(),
|
||||
'http', 'https' => $this->isNonEmptyHostUri(),
|
||||
'ws', 'wss' => $this->isNonEmptyHostUriWithoutFragment(),
|
||||
default => true,
|
||||
}) {
|
||||
throw new SyntaxError('The uri `'.$this.'` is invalid for the `'.$this->scheme.'` scheme.');
|
||||
}
|
||||
|
||||
throw new SyntaxError(sprintf('The uri `%s` is invalid for the `%s` scheme.', (string) $this, $this->scheme));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1019,11 +1001,6 @@ final class Uri implements UriInterface
|
||||
* Generate the URI string representation from its components.
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-5.3
|
||||
*
|
||||
* @param ?string $scheme
|
||||
* @param ?string $authority
|
||||
* @param ?string $query
|
||||
* @param ?string $fragment
|
||||
*/
|
||||
private function getUriString(
|
||||
?string $scheme,
|
||||
@ -1053,15 +1030,13 @@ final class Uri implements UriInterface
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
$this->uri = $this->uri ?? $this->getUriString(
|
||||
return $this->uri ??= $this->getUriString(
|
||||
$this->scheme,
|
||||
$this->authority,
|
||||
$this->path,
|
||||
$this->query,
|
||||
$this->fragment
|
||||
);
|
||||
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1081,9 +1056,15 @@ final class Uri implements UriInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array{scheme:?string, user_info:?string, host:?string, port:?int, path:string, query:?string, fragment:?string}
|
||||
* @return array{
|
||||
* scheme:?string,
|
||||
* user_info:?string,
|
||||
* host:?string,
|
||||
* port:?int,
|
||||
* path:string,
|
||||
* query:?string,
|
||||
* fragment:?string
|
||||
* }
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
@ -1143,7 +1124,7 @@ final class Uri implements UriInterface
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
if (0 === strpos($this->path, '//')) {
|
||||
if (str_starts_with($this->path, '//')) {
|
||||
return '/'.ltrim($this->path, '/');
|
||||
}
|
||||
|
||||
@ -1192,18 +1173,14 @@ final class Uri implements UriInterface
|
||||
*
|
||||
* @throws SyntaxError if the submitted data can not be converted to string
|
||||
*/
|
||||
private function filterString($str): ?string
|
||||
private function filterString(mixed $str): ?string
|
||||
{
|
||||
if (null === $str) {
|
||||
return $str;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_object($str) && method_exists($str, '__toString')) {
|
||||
$str = (string) $str;
|
||||
}
|
||||
|
||||
if (!is_scalar($str)) {
|
||||
throw new SyntaxError(sprintf('The component must be a string, a scalar or a stringable object; `%s` given.', gettype($str)));
|
||||
if (!is_scalar($str) && !$str instanceof Stringable) {
|
||||
throw new SyntaxError('The component must be a string, a scalar or a Stringable object; `'.gettype($str).'` given.');
|
||||
}
|
||||
|
||||
$str = (string) $str;
|
||||
@ -1211,14 +1188,16 @@ final class Uri implements UriInterface
|
||||
return $str;
|
||||
}
|
||||
|
||||
throw new SyntaxError(sprintf('The component `%s` contains invalid characters.', $str));
|
||||
throw new SyntaxError('The component `'.$str.'` contains invalid characters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function withUserInfo($user, $password = null): UriInterface
|
||||
{
|
||||
public function withUserInfo(
|
||||
$user,
|
||||
#[SensitiveParameter] $password = null
|
||||
): UriInterface {
|
||||
$user_info = null;
|
||||
$user = $this->filterString($user);
|
||||
if (null !== $password) {
|
||||
|
||||
@ -15,18 +15,15 @@ namespace League\Uri;
|
||||
|
||||
use League\Uri\Contracts\UriInterface;
|
||||
use Psr\Http\Message\UriInterface as Psr7UriInterface;
|
||||
use TypeError;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function preg_replace_callback;
|
||||
use function rawurldecode;
|
||||
use function sprintf;
|
||||
|
||||
final class UriInfo
|
||||
{
|
||||
private const REGEXP_ENCODED_CHARS = ',%(2[D|E]|3[0-9]|4[1-9|A-F]|5[0-9|AF]|6[1-9|A-F]|7[0-9|E]),i';
|
||||
|
||||
private const WHATWG_SPECIAL_SCHEMES = ['ftp' => 21, 'http' => 80, 'https' => 443, 'ws' => 80, 'wss' => 443];
|
||||
private const REGEXP_ENCODED_CHARS = ',%(2[D|E]|3\d|4[1-9|A-F]|5[\d|A|F]|6[1-9|A-F]|7[\d|E]),i';
|
||||
private const WHATWG_SPECIAL_SCHEMES = ['ftp', 'http', 'https', 'ws', 'wss'];
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
@ -35,46 +32,17 @@ final class UriInfo
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*/
|
||||
private static function emptyComponentValue($uri): ?string
|
||||
|
||||
private static function emptyComponentValue(Psr7UriInterface|UriInterface $uri): ?string
|
||||
{
|
||||
return $uri instanceof Psr7UriInterface ? '' : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the URI object.
|
||||
*
|
||||
* To be valid an URI MUST implement at least one of the following interface:
|
||||
* - League\Uri\UriInterface
|
||||
* - Psr\Http\Message\UriInterface
|
||||
*
|
||||
* @param mixed $uri the URI to validate
|
||||
*
|
||||
* @throws TypeError if the URI object does not implements the supported interfaces.
|
||||
*
|
||||
* @return Psr7UriInterface|UriInterface
|
||||
* Normalizes an URI for comparison.
|
||||
*/
|
||||
private static function filterUri($uri)
|
||||
private static function normalize(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
|
||||
{
|
||||
if ($uri instanceof Psr7UriInterface || $uri instanceof UriInterface) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
throw new TypeError(sprintf('The uri must be a valid URI object received `%s`', is_object($uri) ? get_class($uri) : gettype($uri)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an URI for comparison.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*
|
||||
* @return Psr7UriInterface|UriInterface
|
||||
*/
|
||||
private static function normalize($uri)
|
||||
{
|
||||
$uri = self::filterUri($uri);
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
$path = $uri->getPath();
|
||||
@ -107,36 +75,28 @@ final class UriInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the URI represents an absolute URI.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* Tells whether the URI represents an absolute URI.
|
||||
*/
|
||||
public static function isAbsolute($uri): bool
|
||||
public static function isAbsolute(Psr7UriInterface|UriInterface $uri): bool
|
||||
{
|
||||
return self::emptyComponentValue($uri) !== self::filterUri($uri)->getScheme();
|
||||
return self::emptyComponentValue($uri) !== $uri->getScheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the URI represents a network path.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*/
|
||||
public static function isNetworkPath($uri): bool
|
||||
public static function isNetworkPath(Psr7UriInterface|UriInterface $uri): bool
|
||||
{
|
||||
$uri = self::filterUri($uri);
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
return $null === $uri->getScheme() && $null !== $uri->getAuthority();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the URI represents an absolute path.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* Tells whether the URI represents an absolute path.
|
||||
*/
|
||||
public static function isAbsolutePath($uri): bool
|
||||
public static function isAbsolutePath(Psr7UriInterface|UriInterface $uri): bool
|
||||
{
|
||||
$uri = self::filterUri($uri);
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
return $null === $uri->getScheme()
|
||||
@ -147,11 +107,9 @@ final class UriInfo
|
||||
/**
|
||||
* Tell whether the URI represents a relative path.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*/
|
||||
public static function isRelativePath($uri): bool
|
||||
public static function isRelativePath(Psr7UriInterface|UriInterface $uri): bool
|
||||
{
|
||||
$uri = self::filterUri($uri);
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
return $null === $uri->getScheme()
|
||||
@ -160,12 +118,9 @@ final class UriInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether both URI refers to the same document.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
* Tells whether both URI refers to the same document.
|
||||
*/
|
||||
public static function isSameDocument($uri, $base_uri): bool
|
||||
public static function isSameDocument(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $base_uri): bool
|
||||
{
|
||||
$uri = self::normalize($uri);
|
||||
$base_uri = self::normalize($base_uri);
|
||||
@ -182,31 +137,30 @@ final class UriInfo
|
||||
* For URI without a special scheme the method returns null
|
||||
* For URI with the file scheme the method will return null (as this is left to the implementation decision)
|
||||
* For URI with a special scheme the method returns the scheme followed by its authority (without the userinfo part)
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*/
|
||||
public static function getOrigin($uri): ?string
|
||||
public static function getOrigin(Psr7UriInterface|UriInterface $uri): ?string
|
||||
{
|
||||
$scheme = self::filterUri($uri)->getScheme();
|
||||
$scheme = $uri->getScheme();
|
||||
if ('blob' === $scheme) {
|
||||
$uri = Uri::createFromString($uri->getPath());
|
||||
$scheme = $uri->getScheme();
|
||||
}
|
||||
|
||||
if (null === $scheme || !array_key_exists($scheme, self::WHATWG_SPECIAL_SCHEMES)) {
|
||||
return null;
|
||||
if (in_array($scheme, self::WHATWG_SPECIAL_SCHEMES, true)) {
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
return (string) $uri->withFragment($null)->withQuery($null)->withPath('')->withUserInfo($null, null);
|
||||
}
|
||||
|
||||
$null = self::emptyComponentValue($uri);
|
||||
|
||||
return (string) $uri->withFragment($null)->withQuery($null)->withPath('')->withUserInfo($null);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
* Tells whether two URI do not share the same origin.
|
||||
*
|
||||
* @see UriInfo::getOrigin()
|
||||
*/
|
||||
public static function isCrossOrigin($uri, $base_uri): bool
|
||||
public static function isCrossOrigin(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $base_uri): bool
|
||||
{
|
||||
return null === ($uriString = self::getOrigin(Uri::createFromUri($uri)))
|
||||
|| null === ($baseUriString = self::getOrigin(Uri::createFromUri($base_uri)))
|
||||
|
||||
@ -15,16 +15,13 @@ namespace League\Uri;
|
||||
|
||||
use League\Uri\Contracts\UriInterface;
|
||||
use Psr\Http\Message\UriInterface as Psr7UriInterface;
|
||||
use TypeError;
|
||||
use function array_pop;
|
||||
use function array_reduce;
|
||||
use function count;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
@ -44,20 +41,13 @@ final class UriResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an URI against a base URI using RFC3986 rules.
|
||||
* Resolves an URI against a base URI using RFC3986 rules.
|
||||
*
|
||||
* If the first argument is a UriInterface the method returns a UriInterface object
|
||||
* If the first argument is a Psr7UriInterface the method returns a Psr7UriInterface object
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
*
|
||||
* @return Psr7UriInterface|UriInterface
|
||||
*/
|
||||
public static function resolve($uri, $base_uri)
|
||||
public static function resolve(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $base_uri): Psr7UriInterface|UriInterface
|
||||
{
|
||||
self::filterUri($uri);
|
||||
self::filterUri($base_uri);
|
||||
$null = $uri instanceof Psr7UriInterface ? '' : null;
|
||||
|
||||
if ($null !== $uri->getScheme()) {
|
||||
@ -90,38 +80,24 @@ final class UriResolver
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the URI object.
|
||||
*
|
||||
* @param mixed $uri an URI object
|
||||
*
|
||||
* @throws TypeError if the URI object does not implements the supported interfaces.
|
||||
*/
|
||||
private static function filterUri($uri): void
|
||||
{
|
||||
if (!$uri instanceof UriInterface && !$uri instanceof Psr7UriInterface) {
|
||||
throw new TypeError(sprintf('The uri must be a valid URI object received `%s`', gettype($uri)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dot segments from the URI path.
|
||||
*/
|
||||
private static function removeDotSegments(string $path): string
|
||||
{
|
||||
if (false === strpos($path, '.')) {
|
||||
if (!str_contains($path, '.')) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$old_segments = explode('/', $path);
|
||||
$new_path = implode('/', array_reduce($old_segments, [UriResolver::class, 'reducer'], []));
|
||||
$new_path = implode('/', array_reduce($old_segments, UriResolver::reducer(...), []));
|
||||
if (isset(self::DOT_SEGMENTS[end($old_segments)])) {
|
||||
$new_path .= '/';
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// added because some PSR-7 implementations do not respect RFC3986
|
||||
if (0 === strpos($path, '/') && 0 !== strpos($new_path, '/')) {
|
||||
if (str_starts_with($path, '/') && !str_starts_with($new_path, '/')) {
|
||||
return '/'.$new_path;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
@ -150,21 +126,20 @@ final class UriResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an URI path and query component.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
* Resolves an URI path and query component.
|
||||
*
|
||||
* @return array{0:string, 1:string|null}
|
||||
*/
|
||||
private static function resolvePathAndQuery($uri, $base_uri): array
|
||||
{
|
||||
private static function resolvePathAndQuery(
|
||||
Psr7UriInterface|UriInterface $uri,
|
||||
Psr7UriInterface|UriInterface $base_uri
|
||||
): array {
|
||||
$target_path = $uri->getPath();
|
||||
$target_query = $uri->getQuery();
|
||||
$null = $uri instanceof Psr7UriInterface ? '' : null;
|
||||
$baseNull = $base_uri instanceof Psr7UriInterface ? '' : null;
|
||||
|
||||
if (0 === strpos($target_path, '/')) {
|
||||
if (str_starts_with($target_path, '/')) {
|
||||
return [$target_path, $target_query];
|
||||
}
|
||||
|
||||
@ -176,7 +151,7 @@ final class UriResolver
|
||||
$target_path = $base_uri->getPath();
|
||||
//@codeCoverageIgnoreStart
|
||||
//because some PSR-7 Uri implementations allow this RFC3986 forbidden construction
|
||||
if ($baseNull !== $base_uri->getAuthority() && 0 !== strpos($target_path, '/')) {
|
||||
if ($baseNull !== $base_uri->getAuthority() && !str_starts_with($target_path, '/')) {
|
||||
$target_path = '/'.$target_path;
|
||||
}
|
||||
//@codeCoverageIgnoreEnd
|
||||
@ -201,23 +176,18 @@ final class UriResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* Relativize an URI according to a base URI.
|
||||
* Relativizes an URI according to a base URI.
|
||||
*
|
||||
* This method MUST retain the state of the submitted URI instance, and return
|
||||
* an URI instance of the same type that contains the applied modifications.
|
||||
*
|
||||
* This method MUST be transparent when dealing with error and exceptions.
|
||||
* It MUST not alter of silence them apart from validating its own parameters.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
*
|
||||
* @return Psr7UriInterface|UriInterface
|
||||
*/
|
||||
public static function relativize($uri, $base_uri)
|
||||
{
|
||||
self::filterUri($uri);
|
||||
self::filterUri($base_uri);
|
||||
public static function relativize(
|
||||
Psr7UriInterface|UriInterface $uri,
|
||||
Psr7UriInterface|UriInterface $base_uri
|
||||
): Psr7UriInterface|UriInterface {
|
||||
$uri = self::formatHost($uri);
|
||||
$base_uri = self::formatHost($base_uri);
|
||||
if (!self::isRelativizable($uri, $base_uri)) {
|
||||
@ -231,7 +201,7 @@ final class UriResolver
|
||||
return $uri->withPath(self::relativizePath($target_path, $base_uri->getPath()));
|
||||
}
|
||||
|
||||
if (self::componentEquals('getQuery', $uri, $base_uri)) {
|
||||
if (self::componentEquals('query', $uri, $base_uri)) {
|
||||
return $uri->withPath('')->withQuery($null);
|
||||
}
|
||||
|
||||
@ -244,23 +214,26 @@ final class UriResolver
|
||||
|
||||
/**
|
||||
* Tells whether the component value from both URI object equals.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
*/
|
||||
private static function componentEquals(string $method, $uri, $base_uri): bool
|
||||
{
|
||||
return self::getComponent($method, $uri) === self::getComponent($method, $base_uri);
|
||||
private static function componentEquals(
|
||||
string $property,
|
||||
Psr7UriInterface|UriInterface $uri,
|
||||
Psr7UriInterface|UriInterface $base_uri
|
||||
): bool {
|
||||
return self::getComponent($property, $uri) === self::getComponent($property, $base_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component value from the submitted URI object.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*/
|
||||
private static function getComponent(string $method, $uri): ?string
|
||||
private static function getComponent(string $property, Psr7UriInterface|UriInterface $uri): ?string
|
||||
{
|
||||
$component = $uri->$method();
|
||||
$component = match ($property) {
|
||||
'query' => $uri->getQuery(),
|
||||
'authority' => $uri->getAuthority(),
|
||||
default => $uri->getScheme(), //scheme
|
||||
};
|
||||
|
||||
if ($uri instanceof Psr7UriInterface && '' === $component) {
|
||||
return null;
|
||||
}
|
||||
@ -270,14 +243,8 @@ final class UriResolver
|
||||
|
||||
/**
|
||||
* Filter the URI object.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
*
|
||||
* @throws TypeError if the URI object does not implements the supported interfaces.
|
||||
*
|
||||
* @return Psr7UriInterface|UriInterface
|
||||
*/
|
||||
private static function formatHost($uri)
|
||||
private static function formatHost(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
|
||||
{
|
||||
if (!$uri instanceof Psr7UriInterface) {
|
||||
return $uri;
|
||||
@ -292,20 +259,19 @@ final class UriResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the submitted URI object can be relativize.
|
||||
*
|
||||
* @param Psr7UriInterface|UriInterface $uri
|
||||
* @param Psr7UriInterface|UriInterface $base_uri
|
||||
* Tells whether the submitted URI object can be relativize.
|
||||
*/
|
||||
private static function isRelativizable($uri, $base_uri): bool
|
||||
{
|
||||
private static function isRelativizable(
|
||||
Psr7UriInterface|UriInterface $uri,
|
||||
Psr7UriInterface|UriInterface $base_uri
|
||||
): bool {
|
||||
return !UriInfo::isRelativePath($uri)
|
||||
&& self::componentEquals('getScheme', $uri, $base_uri)
|
||||
&& self::componentEquals('getAuthority', $uri, $base_uri);
|
||||
&& self::componentEquals('scheme', $uri, $base_uri)
|
||||
&& self::componentEquals('authority', $uri, $base_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative the URI for a authority-less target URI.
|
||||
* Relatives the URI for an authority-less target URI.
|
||||
*/
|
||||
private static function relativizePath(string $path, string $basepath): string
|
||||
{
|
||||
|
||||
@ -17,15 +17,11 @@ use League\Uri\Exceptions\IdnaConversionFailed;
|
||||
use League\Uri\Exceptions\IdnSupportMissing;
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\Idna\Idna;
|
||||
use TypeError;
|
||||
use Stringable;
|
||||
use function array_merge;
|
||||
use function explode;
|
||||
use function filter_var;
|
||||
use function gettype;
|
||||
use function inet_pton;
|
||||
use function is_object;
|
||||
use function is_scalar;
|
||||
use function method_exists;
|
||||
use function preg_match;
|
||||
use function rawurldecode;
|
||||
use function sprintf;
|
||||
@ -87,7 +83,7 @@ final class UriString
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
*/
|
||||
private const REGEXP_URI_SCHEME = '/^([a-z][a-z\d\+\.\-]*)?$/i';
|
||||
private const REGEXP_URI_SCHEME = '/^([a-z][a-z\d+.-]*)?$/i';
|
||||
|
||||
/**
|
||||
* IPvFuture regular expression.
|
||||
@ -157,16 +153,7 @@ final class UriString
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-5.3
|
||||
* @link https://tools.ietf.org/html/rfc3986#section-7.5
|
||||
*
|
||||
* @param array{
|
||||
* scheme:?string,
|
||||
* user:?string,
|
||||
* pass:?string,
|
||||
* host:?string,
|
||||
* port:?int,
|
||||
* path:?string,
|
||||
* query:?string,
|
||||
* fragment:?string
|
||||
* } $components
|
||||
* @param array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:?string, query:?string, fragment:?string} $components
|
||||
*/
|
||||
public static function build(array $components): string
|
||||
{
|
||||
@ -242,35 +229,17 @@ final class UriString
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3986
|
||||
*
|
||||
* @param mixed $uri any scalar or stringable object
|
||||
* @param Stringable|string|int|float $uri any scalar or stringable object
|
||||
*
|
||||
* @throws SyntaxError if the URI contains invalid characters
|
||||
* @throws SyntaxError if the URI contains an invalid scheme
|
||||
* @throws SyntaxError if the URI contains an invalid path
|
||||
*
|
||||
* @return array{
|
||||
* scheme:?string,
|
||||
* user:?string,
|
||||
* pass:?string,
|
||||
* host:?string,
|
||||
* port:?int,
|
||||
* path:string,
|
||||
* query:?string,
|
||||
* fragment:?string
|
||||
* }
|
||||
* @return array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:string, query:?string, fragment:?string}
|
||||
*/
|
||||
public static function parse($uri): array
|
||||
public static function parse(Stringable|string|int|float $uri): array
|
||||
{
|
||||
if (is_object($uri) && method_exists($uri, '__toString')) {
|
||||
$uri = (string) $uri;
|
||||
}
|
||||
|
||||
if (!is_scalar($uri)) {
|
||||
throw new TypeError(sprintf('The uri must be a scalar or a stringable object `%s` given', gettype($uri)));
|
||||
}
|
||||
|
||||
$uri = (string) $uri;
|
||||
|
||||
if (isset(self::URI_SCHORTCUTS[$uri])) {
|
||||
/** @var array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:string, query:?string, fragment:?string} $components */
|
||||
$components = array_merge(self::URI_COMPONENTS, self::URI_SCHORTCUTS[$uri]);
|
||||
@ -395,7 +364,7 @@ final class UriString
|
||||
return $host;
|
||||
}
|
||||
|
||||
if ('[' !== $host[0] || ']' !== substr($host, -1)) {
|
||||
if ('[' !== $host[0] || !str_ends_with($host, ']')) {
|
||||
return self::filterRegisteredName($host);
|
||||
}
|
||||
|
||||
@ -462,6 +431,6 @@ final class UriString
|
||||
$ip_host = substr($ip_host, 0, $pos);
|
||||
|
||||
return false !== filter_var($ip_host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
|
||||
&& 0 === strpos((string) inet_pton($ip_host), self::ZONE_ID_ADDRESS_BLOCK);
|
||||
&& str_starts_with((string)inet_pton($ip_host), self::ZONE_ID_ADDRESS_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
|
||||
use League\Uri\UriTemplate\Template;
|
||||
use League\Uri\UriTemplate\VariableBag;
|
||||
use TypeError;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Defines the URI Template syntax and the process for expanding a URI Template into a URI reference.
|
||||
@ -34,25 +34,22 @@ use TypeError;
|
||||
*/
|
||||
final class UriTemplate
|
||||
{
|
||||
private Template $template;
|
||||
private VariableBag $defaultVariables;
|
||||
public readonly Template $template;
|
||||
public readonly VariableBag $defaultVariables;
|
||||
|
||||
/**
|
||||
* @param object|string $template a string or an object with the __toString method
|
||||
*
|
||||
* @throws TypeError if the template is not a string or an object with the __toString method
|
||||
* @throws SyntaxError if the template syntax is invalid
|
||||
* @throws TemplateCanNotBeExpanded if the template variables are invalid
|
||||
*/
|
||||
public function __construct($template, array $defaultVariables = [])
|
||||
public function __construct(Template|Stringable|string $template, VariableBag|array $defaultVariables = [])
|
||||
{
|
||||
$this->template = Template::createFromString($template);
|
||||
$this->template = $template instanceof Template ? $template : Template::createFromString($template);
|
||||
$this->defaultVariables = $this->filterVariables($defaultVariables);
|
||||
}
|
||||
|
||||
public static function __set_state(array $properties): self
|
||||
{
|
||||
return new self($properties['template']->toString(), $properties['defaultVariables']->all());
|
||||
return new self($properties['template'], $properties['defaultVariables']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,16 +57,19 @@ final class UriTemplate
|
||||
*
|
||||
* @param array<string,string|array<string>> $variables
|
||||
*/
|
||||
private function filterVariables(array $variables): VariableBag
|
||||
private function filterVariables(VariableBag|array $variables): VariableBag
|
||||
{
|
||||
$output = new VariableBag();
|
||||
foreach ($this->template->variableNames() as $name) {
|
||||
if (isset($variables[$name])) {
|
||||
$output->assign($name, $variables[$name]);
|
||||
}
|
||||
}
|
||||
return array_reduce(
|
||||
$this->template->variableNames,
|
||||
function (VariableBag $curry, string $name) use ($variables): VariableBag {
|
||||
if (isset($variables[$name])) {
|
||||
$curry[$name] = $variables[$name];
|
||||
}
|
||||
|
||||
return $output;
|
||||
return $curry;
|
||||
},
|
||||
new VariableBag()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +77,7 @@ final class UriTemplate
|
||||
*/
|
||||
public function getTemplate(): string
|
||||
{
|
||||
return $this->template->toString();
|
||||
return $this->template->value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +87,7 @@ final class UriTemplate
|
||||
*/
|
||||
public function getVariableNames(): array
|
||||
{
|
||||
return $this->template->variableNames();
|
||||
return $this->template->variableNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,19 +111,16 @@ final class UriTemplate
|
||||
* If present, variables whose name is not part of the current template
|
||||
* possible variable names are removed.
|
||||
*/
|
||||
public function withDefaultVariables(array $defaultDefaultVariables): self
|
||||
public function withDefaultVariables(VariableBag|array $defaultDefaultVariables): self
|
||||
{
|
||||
return new self(
|
||||
$this->template->toString(),
|
||||
$this->filterVariables($defaultDefaultVariables)->all()
|
||||
);
|
||||
return new self($this->template, $defaultDefaultVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TemplateCanNotBeExpanded if the variable contains nested array values
|
||||
* @throws UriException if the resulting expansion can not be converted to a UriInterface instance
|
||||
*/
|
||||
public function expand(array $variables = []): UriInterface
|
||||
public function expand(VariableBag|array $variables = []): UriInterface
|
||||
{
|
||||
return Uri::createFromString(
|
||||
$this->template->expand(
|
||||
|
||||
@ -16,7 +16,6 @@ namespace League\Uri\UriTemplate;
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
|
||||
use function array_filter;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_unique;
|
||||
use function explode;
|
||||
@ -24,7 +23,6 @@ use function implode;
|
||||
use function preg_match;
|
||||
use function rawurlencode;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
|
||||
final class Expression
|
||||
@ -64,46 +62,29 @@ final class Expression
|
||||
'&' => ['prefix' => '&', 'joiner' => '&', 'query' => true],
|
||||
];
|
||||
|
||||
private string $operator;
|
||||
/** @var array<VarSpecifier> */
|
||||
private array $varSpecifiers;
|
||||
private string $joiner;
|
||||
/** @var array<string> */
|
||||
private array $variableNames;
|
||||
private string $expressionString;
|
||||
public readonly array $variableNames;
|
||||
public readonly string $value;
|
||||
|
||||
private function __construct(string $operator, VarSpecifier ...$varSpecifiers)
|
||||
private function __construct(private string $operator, VarSpecifier ...$varSpecifiers)
|
||||
{
|
||||
$this->operator = $operator;
|
||||
$this->varSpecifiers = $varSpecifiers;
|
||||
$this->joiner = self::OPERATOR_HASH_LOOKUP[$operator]['joiner'];
|
||||
$this->variableNames = $this->setVariableNames();
|
||||
$this->expressionString = $this->setExpressionString();
|
||||
$this->variableNames = array_unique(array_map(
|
||||
static fn (VarSpecifier $varSpecifier): string => $varSpecifier->name,
|
||||
$varSpecifiers
|
||||
));
|
||||
$this->value = '{'.$operator.implode(',', array_map(
|
||||
static fn (VarSpecifier $varSpecifier): string => $varSpecifier->toString(),
|
||||
$varSpecifiers
|
||||
)).'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
private function setVariableNames(): array
|
||||
{
|
||||
return array_unique(array_map(
|
||||
static fn (VarSpecifier $varSpecifier): string => $varSpecifier->name(),
|
||||
$this->varSpecifiers
|
||||
));
|
||||
}
|
||||
|
||||
private function setExpressionString(): string
|
||||
{
|
||||
$varSpecifierString = implode(',', array_map(
|
||||
static fn (VarSpecifier $variable): string => $variable->toString(),
|
||||
$this->varSpecifiers
|
||||
));
|
||||
|
||||
return '{'.$this->operator.$varSpecifierString.'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @param array{operator:string, varSpecifiers:array<VarSpecifier>} $properties
|
||||
*/
|
||||
public static function __set_state(array $properties): self
|
||||
{
|
||||
@ -123,7 +104,7 @@ final class Expression
|
||||
|
||||
/** @var array{operator:string, variables:string} $parts */
|
||||
$parts = $parts + ['operator' => ''];
|
||||
if ('' !== $parts['operator'] && false !== strpos(self::RESERVED_OPERATOR, $parts['operator'])) {
|
||||
if ('' !== $parts['operator'] && str_contains(self::RESERVED_OPERATOR, $parts['operator'])) {
|
||||
throw new SyntaxError('The operator used in the expression "'.$expression.'" is reserved.');
|
||||
}
|
||||
|
||||
@ -136,13 +117,18 @@ final class Expression
|
||||
/**
|
||||
* Returns the expression string representation.
|
||||
*
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->expressionString;
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function variableNames(): array
|
||||
@ -178,7 +164,7 @@ final class Expression
|
||||
*/
|
||||
private function replace(VarSpecifier $varSpec, VariableBag $variables): string
|
||||
{
|
||||
$value = $variables->fetch($varSpec->name());
|
||||
$value = $variables->fetch($varSpec->name);
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
@ -190,10 +176,10 @@ final class Expression
|
||||
}
|
||||
|
||||
if ('&' !== $this->joiner && '' === $expanded) {
|
||||
return $varSpec->name();
|
||||
return $varSpec->name;
|
||||
}
|
||||
|
||||
return $varSpec->name().'='.$expanded;
|
||||
return $varSpec->name.'='.$expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,7 +187,7 @@ final class Expression
|
||||
*
|
||||
* @return array{0:string, 1:bool}
|
||||
*/
|
||||
private function inject($value, VarSpecifier $varSpec, bool $useQuery): array
|
||||
private function inject(array|string $value, VarSpecifier $varSpec, bool $useQuery): array
|
||||
{
|
||||
if (is_string($value)) {
|
||||
return $this->replaceString($value, $varSpec, $useQuery);
|
||||
@ -217,16 +203,15 @@ final class Expression
|
||||
*/
|
||||
private function replaceString(string $value, VarSpecifier $varSpec, bool $useQuery): array
|
||||
{
|
||||
if (':' === $varSpec->modifier()) {
|
||||
$value = substr($value, 0, $varSpec->position());
|
||||
if (':' === $varSpec->modifier) {
|
||||
$value = substr($value, 0, $varSpec->position);
|
||||
}
|
||||
|
||||
$expanded = rawurlencode($value);
|
||||
if ('+' === $this->operator || '#' === $this->operator) {
|
||||
return [$this->decodeReserved($expanded), $useQuery];
|
||||
if (in_array($this->operator, ['+', '#'], true)) {
|
||||
return [$this->decodeReserved(rawurlencode($value)), $useQuery];
|
||||
}
|
||||
|
||||
return [$expanded, $useQuery];
|
||||
return [rawurlencode($value), $useQuery];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,48 +229,45 @@ final class Expression
|
||||
return ['', false];
|
||||
}
|
||||
|
||||
if (':' === $varSpec->modifier()) {
|
||||
throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name());
|
||||
if (':' === $varSpec->modifier) {
|
||||
throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name);
|
||||
}
|
||||
|
||||
$pairs = [];
|
||||
$isAssoc = $this->isAssoc($value);
|
||||
$isList = array_is_list($value);
|
||||
foreach ($value as $key => $var) {
|
||||
if ($isAssoc) {
|
||||
if (!$isList) {
|
||||
$key = rawurlencode((string) $key);
|
||||
}
|
||||
|
||||
$var = rawurlencode($var);
|
||||
if ('+' === $this->operator || '#' === $this->operator) {
|
||||
if (in_array($this->operator, ['+', '#'], true)) {
|
||||
$var = $this->decodeReserved($var);
|
||||
}
|
||||
|
||||
if ('*' === $varSpec->modifier()) {
|
||||
if ($isAssoc) {
|
||||
if ('*' === $varSpec->modifier) {
|
||||
if (!$isList) {
|
||||
$var = $key.'='.$var;
|
||||
} elseif ($key > 0 && $useQuery) {
|
||||
$var = $varSpec->name().'='.$var;
|
||||
$var = $varSpec->name.'='.$var;
|
||||
}
|
||||
}
|
||||
|
||||
$pairs[$key] = $var;
|
||||
}
|
||||
|
||||
if ('*' === $varSpec->modifier()) {
|
||||
if ($isAssoc) {
|
||||
// Don't prepend the value name when using the explode
|
||||
// modifier with an associative array.
|
||||
if ('*' === $varSpec->modifier) {
|
||||
if (!$isList) {
|
||||
// Don't prepend the value name when using the `explode` modifier with an associative array.
|
||||
$useQuery = false;
|
||||
}
|
||||
|
||||
return [implode($this->joiner, $pairs), $useQuery];
|
||||
}
|
||||
|
||||
if ($isAssoc) {
|
||||
// When an associative array is encountered and the
|
||||
// explode modifier is not set, then the result must be
|
||||
// a comma separated list of keys followed by their
|
||||
// respective values.
|
||||
if (!$isList) {
|
||||
// When an associative array is encountered and the `explode` modifier is not set, then
|
||||
// the result must be a comma separated list of keys followed by their respective values.
|
||||
foreach ($pairs as $offset => &$data) {
|
||||
$data = $offset.','.$data;
|
||||
}
|
||||
@ -296,19 +278,6 @@ final class Expression
|
||||
return [implode(',', $pairs), $useQuery];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an array is associative.
|
||||
*
|
||||
* This makes the assumption that input arrays are sequences or hashes.
|
||||
* This assumption is a trade-off for accuracy in favor of speed, but it
|
||||
* should work in almost every case where input is supplied for a URI
|
||||
* template.
|
||||
*/
|
||||
private function isAssoc(array $array): bool
|
||||
{
|
||||
return [] !== $array && 0 !== array_keys($array)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes percent encoding on reserved characters (used with + and # modifiers).
|
||||
*/
|
||||
|
||||
@ -15,17 +15,11 @@ namespace League\Uri\UriTemplate;
|
||||
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
|
||||
use TypeError;
|
||||
use function array_merge;
|
||||
use Stringable;
|
||||
use function array_unique;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function method_exists;
|
||||
use function preg_match_all;
|
||||
use function preg_replace;
|
||||
use function sprintf;
|
||||
use function strpos;
|
||||
use function str_contains;
|
||||
use const PREG_SET_ORDER;
|
||||
|
||||
final class Template
|
||||
@ -33,53 +27,42 @@ final class Template
|
||||
/**
|
||||
* Expression regular expression pattern.
|
||||
*/
|
||||
private const REGEXP_EXPRESSION_DETECTOR = '/\{[^\}]*\}/x';
|
||||
private const REGEXP_EXPRESSION_DETECTOR = '/\{[^}]*}/x';
|
||||
|
||||
private string $template;
|
||||
/** @var array<string, Expression> */
|
||||
private array $expressions = [];
|
||||
/** @var array<string> */
|
||||
private array $variableNames;
|
||||
public readonly array $variableNames;
|
||||
|
||||
private function __construct(string $template, Expression ...$expressions)
|
||||
private function __construct(public readonly string $value, Expression ...$expressions)
|
||||
{
|
||||
$this->template = $template;
|
||||
$variableNames = [];
|
||||
foreach ($expressions as $expression) {
|
||||
$this->expressions[$expression->toString()] = $expression;
|
||||
$variableNames[] = $expression->variableNames();
|
||||
$this->expressions[$expression->value] = $expression;
|
||||
$variableNames = [...$variableNames, ...$expression->variableNames];
|
||||
}
|
||||
$this->variableNames = array_unique(array_merge([], ...$variableNames));
|
||||
|
||||
$this->variableNames = array_unique($variableNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @param array{value:string, template?:string, expressions:array<string, Expression>} $properties
|
||||
*/
|
||||
public static function __set_state(array $properties): self
|
||||
{
|
||||
return new self($properties['template'], ...array_values($properties['expressions']));
|
||||
return new self($properties['template'] ?? $properties['value'], ...array_values($properties['expressions']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object|string $template a string or an object with the __toString method
|
||||
*
|
||||
* @throws TypeError if the template is not a string or an object with the __toString method
|
||||
* @throws SyntaxError if the template contains invalid expressions
|
||||
* @throws SyntaxError if the template contains invalid variable specification
|
||||
*/
|
||||
public static function createFromString($template): self
|
||||
public static function createFromString(Stringable|string $template): self
|
||||
{
|
||||
if (is_object($template) && method_exists($template, '__toString')) {
|
||||
$template = (string) $template;
|
||||
}
|
||||
|
||||
if (!is_string($template)) {
|
||||
throw new TypeError(sprintf('The template must be a string or a stringable object %s given.', gettype($template)));
|
||||
}
|
||||
|
||||
$template = (string) $template;
|
||||
/** @var string $remainder */
|
||||
$remainder = preg_replace(self::REGEXP_EXPRESSION_DETECTOR, '', $template);
|
||||
if (false !== strpos($remainder, '{') || false !== strpos($remainder, '}')) {
|
||||
if (str_contains($remainder, '{') || str_contains($remainder, '}')) {
|
||||
throw new SyntaxError('The template "'.$template.'" contains invalid expressions.');
|
||||
}
|
||||
|
||||
@ -96,12 +79,19 @@ final class Template
|
||||
return new self($template, ...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->template;
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function variableNames(): array
|
||||
@ -115,7 +105,7 @@ final class Template
|
||||
*/
|
||||
public function expand(VariableBag $variables): string
|
||||
{
|
||||
$uriString = $this->template;
|
||||
$uriString = $this->value;
|
||||
/** @var Expression $expression */
|
||||
foreach ($this->expressions as $pattern => $expression) {
|
||||
$uriString = str_replace($pattern, $expression->expand($variables), $uriString);
|
||||
|
||||
@ -28,19 +28,15 @@ final class VarSpecifier
|
||||
(?<modifier>\:(?<position>\d+)|\*)?
|
||||
$/x';
|
||||
|
||||
private string $name;
|
||||
private string $modifier;
|
||||
private int $position;
|
||||
|
||||
private function __construct(string $name, string $modifier, int $position)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->modifier = $modifier;
|
||||
$this->position = $position;
|
||||
private function __construct(
|
||||
public readonly string $name,
|
||||
public readonly string $modifier,
|
||||
public readonly int $position
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @param array{name: string, modifier:string, position:int} $properties
|
||||
*/
|
||||
public static function __set_state(array $properties): self
|
||||
{
|
||||
@ -79,16 +75,28 @@ final class VarSpecifier
|
||||
return $this->name.$this->modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
*/
|
||||
public function modifier(): string
|
||||
{
|
||||
return $this->modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated since version 6.6.0 use the readonly property instead
|
||||
*/
|
||||
public function position(): int
|
||||
{
|
||||
return $this->position;
|
||||
|
||||
@ -13,17 +13,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace League\Uri\UriTemplate;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
|
||||
use TypeError;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use Stringable;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function is_scalar;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
|
||||
final class VariableBag
|
||||
/**
|
||||
* @implements ArrayAccess<string, string|bool|int|float|array<string|bool|int|float>>
|
||||
*/
|
||||
final class VariableBag implements ArrayAccess, Countable
|
||||
{
|
||||
/**
|
||||
* @var array<string,string|array<string>>
|
||||
@ -40,11 +41,39 @@ final class VariableBag
|
||||
}
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{variables: array<string,string|array<string>>} $properties
|
||||
*/
|
||||
public static function __set_state(array $properties): self
|
||||
{
|
||||
return new self($properties['variables']);
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return array_key_exists($offset, $this->variables);
|
||||
}
|
||||
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
unset($this->variables[$offset]);
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
$this->assign($offset, $value); /* @phpstan-ignore-line */
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->fetch($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,string|array<string>>
|
||||
*/
|
||||
@ -53,55 +82,45 @@ final class VariableBag
|
||||
return $this->variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the bag is empty or not.
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return [] === $this->variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the variable value if none found returns null.
|
||||
*
|
||||
* @return null|string|array<string>
|
||||
*/
|
||||
public function fetch(string $name)
|
||||
public function fetch(string $name): null|string|array
|
||||
{
|
||||
return $this->variables[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|bool|int|float|array<string|bool|int|float> $value
|
||||
* @param string|bool|int|float|null|array<string|bool|int|float> $value
|
||||
*/
|
||||
public function assign(string $name, $value): void
|
||||
public function assign(string $name, string|bool|int|float|array|null $value): void
|
||||
{
|
||||
$this->variables[$name] = $this->normalizeValue($value, $name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value the value to be expanded
|
||||
* @param Stringable|string|float|int|bool|null $value the value to be expanded
|
||||
*
|
||||
* @throws TemplateCanNotBeExpanded if the value contains nested list
|
||||
*
|
||||
* @return string|array<string>
|
||||
*/
|
||||
private function normalizeValue($value, string $name, bool $isNestedListAllowed)
|
||||
private function normalizeValue(Stringable|array|string|float|int|bool|null $value, string $name, bool $isNestedListAllowed): array|string
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return true === $value ? '1' : '0';
|
||||
}
|
||||
|
||||
if (null === $value || is_scalar($value) || (is_object($value) && method_exists($value, '__toString'))) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
throw new TypeError(sprintf('The variable '.$name.' must be NULL, a scalar or a stringable object `%s` given', gettype($value)));
|
||||
}
|
||||
|
||||
if (!$isNestedListAllowed) {
|
||||
throw TemplateCanNotBeExpanded::dueToNestedListOfValue($name);
|
||||
}
|
||||
|
||||
foreach ($value as &$var) {
|
||||
$var = self::normalizeValue($var, $name, false);
|
||||
}
|
||||
unset($var);
|
||||
|
||||
return $value;
|
||||
return match (true) {
|
||||
is_bool($value) => true === $value ? '1' : '0',
|
||||
(null === $value || is_scalar($value) || is_object($value)) => (string) $value,
|
||||
!$isNestedListAllowed => throw TemplateCanNotBeExpanded::dueToNestedListOfValue($name),
|
||||
default => array_map(fn ($var): array|string => self::normalizeValue($var, $name, false), $value),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__.'/src')
|
||||
->in(__DIR__.'/tests');
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
|
||||
return $config->setRules([
|
||||
'@Symfony' => true,
|
||||
'@Symfony:risky' => true,
|
||||
'native_function_invocation' => ['include'=> ['@all']],
|
||||
'native_constant_invocation' => true,
|
||||
'ordered_imports' => true,
|
||||
'declare_strict_types' => false,
|
||||
'linebreak_after_opening_tag' => false,
|
||||
'single_import_per_statement' => false,
|
||||
'blank_line_after_opening_tag' => false,
|
||||
'concat_space' => ['spacing'=>'one'],
|
||||
'phpdoc_align' => ['align'=>'left'],
|
||||
])
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder);
|
||||
@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||
|
||||
## 1.8.2
|
||||
|
||||
- Fix deprecation warnings in PHP 8.4
|
||||
|
||||
## 1.8.1
|
||||
|
||||
- Fix error handling in Stream::getContents()
|
||||
|
||||
## 1.8.0
|
||||
|
||||
- Deprecate HttplugFactory, use Psr17Factory instead
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
path: src/Response.php
|
||||
|
||||
-
|
||||
message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: src/Response.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
path: src/ServerRequest.php
|
||||
|
||||
-
|
||||
message: "#^Strict comparison using \\!\\=\\= between null and null will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: src/ServerRequest.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
path: src/Stream.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 2
|
||||
path: src/UploadedFile.php
|
||||
|
||||
-
|
||||
message: "#^Strict comparison using \\=\\=\\= between false and true will always evaluate to false\\.$#"
|
||||
count: 2
|
||||
path: src/UploadedFile.php
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="4.30.0@d0bc6e25d89f649e4f36a534f330f8bb4643dd69">
|
||||
<file src="src/Stream.php">
|
||||
<NoValue occurrences="1">
|
||||
<code>return \trigger_error((string) $e, \E_USER_ERROR);</code>
|
||||
</NoValue>
|
||||
</file>
|
||||
</files>
|
||||
@ -57,7 +57,7 @@ class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface,
|
||||
return Stream::create($resource);
|
||||
}
|
||||
|
||||
public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
|
||||
public function createUploadedFile(StreamInterface $stream, ?int $size = null, int $error = \UPLOAD_ERR_OK, ?string $clientFilename = null, ?string $clientMediaType = null): UploadedFileInterface
|
||||
{
|
||||
if (null === $size) {
|
||||
$size = $stream->getSize();
|
||||
|
||||
@ -39,7 +39,7 @@ class Response implements ResponseInterface
|
||||
* @param string $version Protocol version
|
||||
* @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
|
||||
*/
|
||||
public function __construct(int $status = 200, array $headers = [], $body = null, string $version = '1.1', string $reason = null)
|
||||
public function __construct(int $status = 200, array $headers = [], $body = null, string $version = '1.1', ?string $reason = null)
|
||||
{
|
||||
// If we got no body, defer initialization of the stream until Response::getBody()
|
||||
if ('' !== $body && null !== $body) {
|
||||
|
||||
@ -260,11 +260,19 @@ class Stream implements StreamInterface
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
|
||||
if (false === $contents = @\stream_get_contents($this->stream)) {
|
||||
throw new \RuntimeException('Unable to read stream contents: ' . (\error_get_last()['message'] ?? ''));
|
||||
}
|
||||
$exception = null;
|
||||
|
||||
return $contents;
|
||||
\set_error_handler(static function ($type, $message) use (&$exception) {
|
||||
throw $exception = new \RuntimeException('Unable to read stream contents: ' . $message);
|
||||
});
|
||||
|
||||
try {
|
||||
return \stream_get_contents($this->stream);
|
||||
} catch (\Throwable $e) {
|
||||
throw $e === $exception ? $e : new \RuntimeException('Unable to read stream contents: ' . $e->getMessage(), 0, $e);
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
Add `source` link in composer.json. No code changes.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
Allow PSR-7 (psr/http-message) 2.0. No code changes.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
Allow installation with PHP 8. No code changes.
|
||||
|
||||
@ -7,6 +7,6 @@ Note that this is not a HTTP Client implementation of its own. It is merely abst
|
||||
|
||||
The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
|
||||
|
||||
[psr-url]: http://www.php-fig.org/psr/psr-18
|
||||
[psr-url]: https://www.php-fig.org/psr/psr-18
|
||||
[package-url]: https://packagist.org/packages/psr/http-client
|
||||
[implementation-url]: https://packagist.org/providers/psr/http-client-implementation
|
||||
|
||||
@ -7,12 +7,15 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0"
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2019 Fabien Potencier
|
||||
Copyright (c) 2018-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
@ -30,9 +30,6 @@
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2020 Fabien Potencier
|
||||
Copyright (c) 2020-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Polyfill\\Php80\\": "" },
|
||||
@ -29,9 +29,6 @@
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2021 Fabien Potencier
|
||||
Copyright (c) 2021-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -7,6 +7,7 @@ This component provides features added to PHP 8.1 core:
|
||||
- [`enum_exists`](https://php.net/enum-exists)
|
||||
- [`MYSQLI_REFRESH_REPLICA`](https://php.net/mysqli.constants#constantmysqli-refresh-replica) constant
|
||||
- [`ReturnTypeWillChange`](https://wiki.php.net/rfc/internal_method_return_types)
|
||||
- [`CURLStringFile`](https://php.net/CURLStringFile) (but only if PHP >= 7.4 is used)
|
||||
|
||||
More information can be found in the
|
||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (\PHP_VERSION_ID >= 70400 && extension_loaded('curl')) {
|
||||
/**
|
||||
* @property string $data
|
||||
*/
|
||||
class CURLStringFile extends CURLFile
|
||||
{
|
||||
private $data;
|
||||
|
||||
public function __construct(string $data, string $postname, string $mime = 'application/octet-stream')
|
||||
{
|
||||
$this->data = $data;
|
||||
parent::__construct('data://application/octet-stream;base64,'.base64_encode($data), $mime, $postname);
|
||||
}
|
||||
|
||||
public function __set(string $name, $value): void
|
||||
{
|
||||
if ('data' !== $name) {
|
||||
$this->$name = $value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_object($value) ? !method_exists($value, '__toString') : !is_scalar($value)) {
|
||||
throw new TypeError('Cannot assign '.gettype($value).' to property CURLStringFile::$data of type string');
|
||||
}
|
||||
|
||||
$this->name = 'data://application/octet-stream;base64,'.base64_encode($value);
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return isset($this->$name);
|
||||
}
|
||||
|
||||
public function &__get(string $name)
|
||||
{
|
||||
return $this->$name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,9 +25,6 @@
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
|
||||
@ -46,7 +46,7 @@ class ExecutableFinder
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function find(string $name, string $default = null, array $extraDirs = [])
|
||||
public function find(string $name, ?string $default = null, array $extraDirs = [])
|
||||
{
|
||||
if (\ini_get('open_basedir')) {
|
||||
$searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs);
|
||||
|
||||
@ -30,7 +30,7 @@ class InputStream implements \IteratorAggregate
|
||||
/**
|
||||
* Sets a callback that is called when the write buffer becomes empty.
|
||||
*/
|
||||
public function onEmpty(callable $onEmpty = null)
|
||||
public function onEmpty(?callable $onEmpty = null)
|
||||
{
|
||||
$this->onEmpty = $onEmpty;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ class PhpExecutableFinder
|
||||
{
|
||||
if ($php = getenv('PHP_BINARY')) {
|
||||
if (!is_executable($php)) {
|
||||
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v';
|
||||
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v --';
|
||||
if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) {
|
||||
if (!is_executable($php)) {
|
||||
return false;
|
||||
|
||||
@ -32,7 +32,7 @@ class PhpProcess extends Process
|
||||
* @param int $timeout The timeout in seconds
|
||||
* @param array|null $php Path to the PHP binary to use with any additional arguments
|
||||
*/
|
||||
public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null)
|
||||
public function __construct(string $script, ?string $cwd = null, ?array $env = null, int $timeout = 60, ?array $php = null)
|
||||
{
|
||||
if (null === $php) {
|
||||
$executableFinder = new PhpExecutableFinder();
|
||||
@ -53,7 +53,7 @@ class PhpProcess extends Process
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
|
||||
public static function fromShellCommandline(string $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60)
|
||||
{
|
||||
throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class));
|
||||
}
|
||||
@ -61,7 +61,7 @@ class PhpProcess extends Process
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function start(callable $callback = null, array $env = [])
|
||||
public function start(?callable $callback = null, array $env = [])
|
||||
{
|
||||
if (null === $this->getCommandLine()) {
|
||||
throw new RuntimeException('Unable to find the PHP executable.');
|
||||
|
||||
@ -149,7 +149,7 @@ class WindowsPipes extends AbstractPipes
|
||||
if ($w) {
|
||||
@stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6);
|
||||
} elseif ($this->fileHandles) {
|
||||
usleep(Process::TIMEOUT_PRECISION * 1E6);
|
||||
usleep((int) (Process::TIMEOUT_PRECISION * 1E6));
|
||||
}
|
||||
}
|
||||
foreach ($this->fileHandles as $type => $fileHandle) {
|
||||
|
||||
@ -80,6 +80,7 @@ class Process implements \IteratorAggregate
|
||||
private $processPipes;
|
||||
|
||||
private $latestSignal;
|
||||
private $cachedExitCode;
|
||||
|
||||
private static $sigchild;
|
||||
|
||||
@ -140,7 +141,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @throws LogicException When proc_open is not installed
|
||||
*/
|
||||
public function __construct(array $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
|
||||
public function __construct(array $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60)
|
||||
{
|
||||
if (!\function_exists('proc_open')) {
|
||||
throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.');
|
||||
@ -189,7 +190,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @throws LogicException When proc_open is not installed
|
||||
*/
|
||||
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
|
||||
public static function fromShellCommandline(string $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60)
|
||||
{
|
||||
$process = new static([], $cwd, $env, $input, $timeout);
|
||||
$process->commandline = $command;
|
||||
@ -247,7 +248,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function run(callable $callback = null, array $env = []): int
|
||||
public function run(?callable $callback = null, array $env = []): int
|
||||
{
|
||||
$this->start($callback, $env);
|
||||
|
||||
@ -266,7 +267,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function mustRun(callable $callback = null, array $env = []): self
|
||||
public function mustRun(?callable $callback = null, array $env = []): self
|
||||
{
|
||||
if (0 !== $this->run($callback, $env)) {
|
||||
throw new ProcessFailedException($this);
|
||||
@ -294,7 +295,7 @@ class Process implements \IteratorAggregate
|
||||
* @throws RuntimeException When process is already running
|
||||
* @throws LogicException In case a callback is provided and output has been disabled
|
||||
*/
|
||||
public function start(callable $callback = null, array $env = [])
|
||||
public function start(?callable $callback = null, array $env = [])
|
||||
{
|
||||
if ($this->isRunning()) {
|
||||
throw new RuntimeException('Process is already running.');
|
||||
@ -385,7 +386,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function restart(callable $callback = null, array $env = []): self
|
||||
public function restart(?callable $callback = null, array $env = []): self
|
||||
{
|
||||
if ($this->isRunning()) {
|
||||
throw new RuntimeException('Process is already running.');
|
||||
@ -412,7 +413,7 @@ class Process implements \IteratorAggregate
|
||||
* @throws ProcessSignaledException When process stopped after receiving signal
|
||||
* @throws LogicException When process is not yet started
|
||||
*/
|
||||
public function wait(callable $callback = null)
|
||||
public function wait(?callable $callback = null)
|
||||
{
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
@ -914,7 +915,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @return int|null The exit-code of the process or null if it's not running
|
||||
*/
|
||||
public function stop(float $timeout = 10, int $signal = null)
|
||||
public function stop(float $timeout = 10, ?int $signal = null)
|
||||
{
|
||||
$timeoutMicro = microtime(true) + $timeout;
|
||||
if ($this->isRunning()) {
|
||||
@ -1310,7 +1311,7 @@ class Process implements \IteratorAggregate
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function buildCallback(callable $callback = null)
|
||||
protected function buildCallback(?callable $callback = null)
|
||||
{
|
||||
if ($this->outputDisabled) {
|
||||
return function ($type, $data) use ($callback): bool {
|
||||
@ -1345,6 +1346,19 @@ class Process implements \IteratorAggregate
|
||||
$this->processInformation = proc_get_status($this->process);
|
||||
$running = $this->processInformation['running'];
|
||||
|
||||
// In PHP < 8.3, "proc_get_status" only returns the correct exit status on the first call.
|
||||
// Subsequent calls return -1 as the process is discarded. This workaround caches the first
|
||||
// retrieved exit status for consistent results in later calls, mimicking PHP 8.3 behavior.
|
||||
if (\PHP_VERSION_ID < 80300) {
|
||||
if (!isset($this->cachedExitCode) && !$running && -1 !== $this->processInformation['exitcode']) {
|
||||
$this->cachedExitCode = $this->processInformation['exitcode'];
|
||||
}
|
||||
|
||||
if (isset($this->cachedExitCode) && !$running && -1 === $this->processInformation['exitcode']) {
|
||||
$this->processInformation['exitcode'] = $this->cachedExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
$this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);
|
||||
|
||||
if ($this->fallbackStatus && $this->isSigchildEnabled()) {
|
||||
|
||||
Reference in New Issue
Block a user