Skip to content

Commit 71c40c0

Browse files
Introduce PHP 8.5 polyfill
1 parent 731f6e1 commit 71c40c0

16 files changed

+824
-0
lines changed
+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/Php85.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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;
13+
14+
/**
15+
* @author Alexandre Daubois <alex.daubois@gmail.com>
16+
*
17+
* @internal
18+
*/
19+
final class Php85
20+
{
21+
}

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 <fabien@symfony.com>
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

+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 <fabien@symfony.com>
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+
abstract class Uri extends \Symfony\Polyfill\Php85\Uri\Uri
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 <fabien@symfony.com>
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 <fabien@symfony.com>
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 <alex.daubois@gmail.com>
9+
*
10+
* @internal
11+
*/
12+
class Rfc3986Uri extends 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

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

0 commit comments

Comments
 (0)