Skip to content

Commit fdbffb2

Browse files
Introduce PHP 8.5 polyfill
1 parent 731f6e1 commit fdbffb2

15 files changed

+789
-0
lines changed

composer.json

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"symfony/polyfill-php82": "self.version",
3434
"symfony/polyfill-php83": "self.version",
3535
"symfony/polyfill-php84": "self.version",
36+
"symfony/polyfill-php85": "self.version",
3637
"symfony/polyfill-iconv": "self.version",
3738
"symfony/polyfill-intl-grapheme": "self.version",
3839
"symfony/polyfill-intl-icu": "self.version",
@@ -62,6 +63,7 @@
6263
"src/Intl/Icu/Resources/stubs",
6364
"src/Intl/MessageFormatter/Resources/stubs",
6465
"src/Intl/Normalizer/Resources/stubs",
66+
"src/Php85/Resources/stubs",
6567
"src/Php84/Resources/stubs",
6668
"src/Php83/Resources/stubs",
6769
"src/Php82/Resources/stubs",
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Polyfill\Php85\Exception;
4+
5+
class ParsingException extends \RuntimeException
6+
{
7+
}

src/Php85/LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2024-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Php85/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Symfony Polyfill / Php85
2+
========================
3+
4+
This component provides features added to PHP 8.5 core:
5+
6+
- [RFC3986 and WHATWG compliant URI parsing support](https://wiki.php.net/rfc/url_parsing_api)
7+
8+
More information can be found in the
9+
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
10+
11+
License
12+
=======
13+
14+
This library is released under the [MIT license](LICENSE).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Uri;
13+
14+
if (\PHP_VERSION_ID < 80500) {
15+
class Rfc3986Uri extends \Symfony\Polyfill\Php85\Uri\Rfc3986Uri
16+
{
17+
}
18+
}

src/Php85/Resources/stubs/Uri/Uri.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Uri;
13+
14+
use Symfony\Polyfill\Php85 as p;
15+
16+
if (\PHP_VERSION_ID < 80500) {
17+
abstract class Uri extends p\Uri\Uri
18+
{
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Uri;
13+
14+
if (\PHP_VERSION_ID < 80500) {
15+
final class WhatWgError extends \Symfony\Polyfill\Php85\Uri\WhatWgError
16+
{
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Uri;
13+
14+
if (\PHP_VERSION_ID < 80500) {
15+
class WhatWgUri extends \Symfony\Polyfill\Php85\Uri\WhatWgUri
16+
{
17+
}
18+
}

src/Php85/Uri/Rfc3986Uri.php

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Symfony\Polyfill\Php85\Uri;
4+
5+
use Symfony\Polyfill\Php85\Exception\ParsingException;
6+
7+
/**
8+
* @author Alexandre Daubois <[email protected]>
9+
*
10+
* @internal
11+
*/
12+
class Rfc3986Uri extends \Uri\Uri
13+
{
14+
public function __construct(string $uri, ?string $baseUrl = null)
15+
{
16+
if ('' === trim($uri)) {
17+
throw new \ValueError('Argument #1 ($uri) cannot be empty');
18+
}
19+
20+
if (null !== $baseUrl && '' === trim($baseUrl)) {
21+
throw new \ValueError('Argument #2 ($baseUrl) cannot be empty');
22+
}
23+
24+
try {
25+
$this->parse($uri, $baseUrl);
26+
} catch (ParsingException) {
27+
throw new \Error('Argument #1 ($uri) must be a valid URI');
28+
}
29+
}
30+
31+
private function parse(string $uri, ?string $baseUrl): void
32+
{
33+
if (!preg_match('/^[a-zA-Z][a-zA-Z\d+\-.]*:/', $uri) && null !== $baseUrl) {
34+
// uri is a relative uri and bse url exists
35+
$this->parse(rtrim($baseUrl, '/').'/'.ltrim($uri, '/'), null);
36+
37+
return;
38+
}
39+
40+
if (preg_match('/[^\x20-\x7e]/', $uri)) {
41+
// the string contains non-ascii chars
42+
throw new ParsingException();
43+
}
44+
45+
preg_match(self::URI_GLOBAL_REGEX, $uri, $matches);
46+
if (!$matches || !isset($matches['scheme']) || '' === $matches['scheme']) {
47+
//throw new InvalidUriException($uri);
48+
}
49+
50+
if (preg_match('~'.$matches['scheme'].':/(?!/)~', $uri)) {
51+
//throw new InvalidUriException($uri);
52+
}
53+
54+
if (isset($matches['authority'])) {
55+
if (!str_contains($uri, '://') && '' !== $matches['authority']) {
56+
//throw new InvalidUriException($uri);
57+
}
58+
59+
preg_match(self::URI_AUTHORITY_REGEX, $matches['authority'], $authMatches);
60+
61+
$matches = array_merge($matches, $authMatches);
62+
unset($matches['authority']);
63+
}
64+
65+
$matches = array_filter($matches, function (string $value) { return '' !== $value; });
66+
67+
if (isset($matches['host']) && false === \filter_var($matches['host'], FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
68+
// the host contains invalid code points
69+
throw new ParsingException();
70+
}
71+
72+
$this->scheme = $matches['scheme'] ?? null;
73+
$this->user = isset($matches['user']) ? rawurldecode($matches['user']) : null;
74+
$this->password = isset($matches['pass']) ? rawurldecode($matches['pass']) : null;
75+
$this->host = $matches['host'] ?? null;
76+
$this->port = $matches['port'] ?? null;
77+
$this->path = isset($matches['path']) ? ltrim($matches['path'], '/') : null;
78+
$this->query = $matches['query'] ?? null;
79+
$this->fragment = $matches['fragment'] ?? null;
80+
}
81+
}

src/Php85/Uri/Uri.php

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Polyfill\Php85\Uri;
13+
14+
/**
15+
* @author Alexandre Daubois <[email protected]>
16+
*
17+
* @internal
18+
*/
19+
abstract class Uri implements \Stringable
20+
{
21+
protected const URI_GLOBAL_REGEX = '/^(?:(?P<scheme>[^:\/?#]+):)?(?:\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(?:\?(?P<query>[^#]*))?(?:#(?P<fragment>.*))?$/';
22+
protected const URI_AUTHORITY_REGEX = '/^(?:(?P<user>[^:@]*)(?::(?P<pass>[^@]*))?@)?(?P<host>[^:]*)(?::(?P<port>\d*))?$/';
23+
24+
/**
25+
* @var string|null
26+
*/
27+
protected $scheme;
28+
29+
/**
30+
* @var string|null
31+
*/
32+
protected $user;
33+
34+
/**
35+
* @var string|null
36+
*/
37+
protected $password;
38+
39+
/**
40+
* @var string|null
41+
*/
42+
protected $host;
43+
44+
/**
45+
* @var int|null
46+
*/
47+
protected $port;
48+
49+
/**
50+
* @var string|null
51+
*/
52+
protected $path;
53+
54+
/**
55+
* @var string|null
56+
*/
57+
protected $query;
58+
59+
/**
60+
* @var string|null
61+
*/
62+
protected $fragment;
63+
64+
public static function fromRfc3986(string $uri, ?string $baseUrl = null): ?static
65+
{
66+
try {
67+
return new Rfc3986Uri($uri, $baseUrl);
68+
} catch (\ValueError $error) {
69+
throw $error;
70+
} catch (\Error $error) {
71+
return null;
72+
}
73+
}
74+
75+
/**
76+
* @param array<int, WhatWgError> $errors
77+
*/
78+
public static function fromWhatWg(string $uri, ?string $baseUrl = null, &$errors = null): ?static
79+
{
80+
$uri = new WhatWgUri($uri, $baseUrl, $errors);
81+
82+
if ($errors) {
83+
return null;
84+
}
85+
86+
return $uri;
87+
}
88+
89+
public function getScheme(): ?string
90+
{
91+
return $this->scheme;
92+
}
93+
94+
public function getUser(): ?string
95+
{
96+
return $this->user;
97+
}
98+
99+
public function getPassword(): ?string
100+
{
101+
return $this->password;
102+
}
103+
104+
public function getHost(): ?string
105+
{
106+
return $this->host;
107+
}
108+
109+
public function getPort(): ?int
110+
{
111+
return $this->port;
112+
}
113+
114+
public function getPath(): ?string
115+
{
116+
return $this->path;
117+
}
118+
119+
public function getQuery(): ?string
120+
{
121+
return $this->query;
122+
}
123+
124+
public function getFragment(): ?string
125+
{
126+
return $this->fragment;
127+
}
128+
129+
public function __toString(): string
130+
{
131+
132+
}
133+
}

0 commit comments

Comments
 (0)