Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 303a8c9

Browse filesBrowse files
[HttpFoundation] Add UrlParser and Url
1 parent ccbdc1a commit 303a8c9
Copy full SHA for 303a8c9

File tree

18 files changed

+421
-81
lines changed
Filter options

18 files changed

+421
-81
lines changed

‎src/Symfony/Component/DependencyInjection/EnvVarProcessor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+8-14Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
1515
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
1616
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
17+
use Symfony\Component\HttpFoundation\UrlParser\UrlParser;
1718

1819
/**
1920
* @author Nicolas Grekas <p@tchwork.com>
@@ -287,27 +288,20 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed
287288
}
288289

289290
if ('url' === $prefix) {
290-
$parsedEnv = parse_url($env);
291-
292-
if (false === $parsedEnv) {
291+
try {
292+
$params = UrlParser::parse($env);
293+
} catch (\InvalidArgumentException) {
293294
throw new RuntimeException(sprintf('Invalid URL in env var "%s".', $name));
294295
}
295-
if (!isset($parsedEnv['scheme'], $parsedEnv['host'])) {
296+
297+
if (null === $params->host) {
296298
throw new RuntimeException(sprintf('Invalid URL env var "%s": schema and host expected, "%s" given.', $name, $env));
297299
}
298-
$parsedEnv += [
299-
'port' => null,
300-
'user' => null,
301-
'pass' => null,
302-
'path' => null,
303-
'query' => null,
304-
'fragment' => null,
305-
];
306300

307301
// remove the '/' separator
308-
$parsedEnv['path'] = '/' === ($parsedEnv['path'] ?? '/') ? '' : substr($parsedEnv['path'], 1);
302+
$params->path = '/' === ($params->path ?? '/') ? '' : substr($params->path, 1);
309303

310-
return $parsedEnv;
304+
return (array) $params;
311305
}
312306

313307
if ('query_string' === $prefix) {

‎src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,12 +624,12 @@ public function testDumpedUrlEnvParameters()
624624
$container = new \Symfony_DI_PhpDumper_Test_UrlParameters();
625625
$this->assertSame([
626626
'scheme' => 'postgres',
627+
'user' => 'user',
628+
'pass' => null,
627629
'host' => 'localhost',
628630
'port' => 5432,
629-
'user' => 'user',
630631
'path' => 'database',
631632
'query' => 'sslmode=disable',
632-
'pass' => null,
633633
'fragment' => null,
634634
], $container->getParameter('hello'));
635635
}

‎src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,8 @@ public function testResolveStringWithSpacesReturnsString($expected, $test, $desc
348348
public static function stringsWithSpacesProvider()
349349
{
350350
return [
351-
['bar', '%foo%', 'Parameters must be wrapped by %.'],
352-
['% foo %', '% foo %', 'Parameters should not have spaces.'],
351+
['bar', '%foo%', 'Url must be wrapped by %.'],
352+
['% foo %', '% foo %', 'Url should not have spaces.'],
353353
['{% set my_template = "foo" %}', '{% set my_template = "foo" %}', 'Twig-like strings are not parameters.'],
354354
['50% is less than 100%', '50% is less than 100%', 'Text between % signs is allowed, if there are spaces.'],
355355
];

‎src/Symfony/Component/HttpFoundation/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Add `UploadedFile::getClientOriginalPath()`
8+
* Add `UrlParser` and `Url`
89

910
7.0
1011
---
+20Lines changed: 20 additions & 0 deletions
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 <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\Component\HttpFoundation\Exception\Parser;
13+
14+
class InvalidUrlException extends \InvalidArgumentException
15+
{
16+
public function __construct(string $dsn)
17+
{
18+
parent::__construct('The URL is invalid.');
19+
}
20+
}
+20Lines changed: 20 additions & 0 deletions
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 <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\Component\HttpFoundation\Exception\Parser;
13+
14+
class MissingHostException extends \InvalidArgumentException
15+
{
16+
public function __construct()
17+
{
18+
parent::__construct('The URL must contain a host.');
19+
}
20+
}
+20Lines changed: 20 additions & 0 deletions
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 <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\Component\HttpFoundation\Exception\Parser;
13+
14+
class MissingSchemeException extends \InvalidArgumentException
15+
{
16+
public function __construct()
17+
{
18+
parent::__construct('The URL must contain a scheme.');
19+
}
20+
}
+74Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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\Component\HttpFoundation\Tests\UrlParser;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\UrlParser\UrlParser;
16+
use Symfony\Component\HttpFoundation\Exception\Parser\InvalidUrlException;
17+
use Symfony\Component\HttpFoundation\Exception\Parser\MissingSchemeException;
18+
19+
class UrlParserTest extends TestCase
20+
{
21+
public function testInvalidDsn()
22+
{
23+
$this->expectException(InvalidUrlException::class);
24+
$this->expectExceptionMessage('The URL is invalid.');
25+
26+
UrlParser::parse('/search:2019');
27+
}
28+
29+
public function testMissingScheme()
30+
{
31+
$this->expectException(MissingSchemeException::class);
32+
$this->expectExceptionMessage('The URL must contain a scheme.');
33+
34+
UrlParser::parse('://example.com');
35+
}
36+
37+
public function testReturnsFullParsedDsn()
38+
{
39+
$parsedDsn = UrlParser::parse('http://user:pass@localhost:8080/path?query=1#fragment');
40+
41+
$this->assertSame('http', $parsedDsn->scheme);
42+
$this->assertSame('user', $parsedDsn->user);
43+
$this->assertSame('pass', $parsedDsn->pass);
44+
$this->assertSame('localhost', $parsedDsn->host);
45+
$this->assertSame(8080, $parsedDsn->port);
46+
$this->assertSame('/path', $parsedDsn->path);
47+
$this->assertSame('query=1', $parsedDsn->query);
48+
$this->assertSame('fragment', $parsedDsn->fragment);
49+
}
50+
51+
public function testItDecodesByDefault()
52+
{
53+
$parsedDsn = UrlParser::parse('http://user%20one:p%40ss@localhost:8080/path?query=1#fragment');
54+
55+
$this->assertSame('user one', $parsedDsn->user);
56+
$this->assertSame('p@ss', $parsedDsn->pass);
57+
}
58+
59+
public function testDisableDecoding()
60+
{
61+
$parsedDsn = UrlParser::parse('http://user%20one:p%40ss@localhost:8080/path?query=1#fragment', decodeAuth: false);
62+
63+
$this->assertSame('user%20one', $parsedDsn->user);
64+
$this->assertSame('p%40ss', $parsedDsn->pass);
65+
}
66+
67+
public function testEmptyUserAndPasswordAreSetToNull()
68+
{
69+
$parsedDsn = UrlParser::parse('http://@localhost:8080/path?query=1#fragment');
70+
71+
$this->assertNull($parsedDsn->user);
72+
$this->assertNull($parsedDsn->pass);
73+
}
74+
}
+68Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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\Component\HttpFoundation\Tests\UrlParser;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\UrlParser\Url;
16+
17+
class UrlTest extends TestCase
18+
{
19+
/**
20+
* @dataProvider provideUserAndPass
21+
*/
22+
public function testIsAuthenticated(?string $user, ?string $pass, bool $expected)
23+
{
24+
$params = new Url('http', $user, $pass);
25+
26+
$this->assertSame($expected, $params->isAuthenticated());
27+
}
28+
29+
public function provideUserAndPass()
30+
{
31+
yield 'no user, no pass' => [null, null, false];
32+
yield 'user, no pass' => ['user', null, true];
33+
yield 'no user, pass' => [null, 'pass', true];
34+
yield 'user, pass' => ['user', 'pass', true];
35+
}
36+
37+
public function testToString()
38+
{
39+
$params = new Url(
40+
'http',
41+
'user',
42+
'pass',
43+
'localhost',
44+
8080,
45+
'/path',
46+
'query=1',
47+
'fragment'
48+
);
49+
50+
$this->assertSame('http://user:pass@localhost:8080/path?query=1#fragment', (string) $params);
51+
}
52+
53+
public function testToStringReencode()
54+
{
55+
$params = new Url(
56+
'http',
57+
'user one',
58+
'p@ss',
59+
'localhost',
60+
8080,
61+
'/p@th',
62+
'query=1',
63+
'fr%40gment%20with%20spaces'
64+
);
65+
66+
$this->assertSame('http://user%20one:p%40ss@localhost:8080/p@th?query=1#fr%40gment%20with%20spaces', (string) $params);
67+
}
68+
}
+80Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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\Component\HttpFoundation\UrlParser;
13+
14+
/**
15+
* @author Alexandre Daubois <alex.daubois@gmail.com>
16+
*/
17+
final class Url implements \Stringable
18+
{
19+
public function __construct(
20+
public string $scheme,
21+
public ?string $user = null,
22+
public ?string $pass = null,
23+
public ?string $host = null,
24+
public ?int $port = null,
25+
public ?string $path = null,
26+
public ?string $query = null,
27+
public ?string $fragment = null
28+
) {
29+
}
30+
31+
public function isAuthenticated(): bool
32+
{
33+
return null !== $this->user || null !== $this->pass;
34+
}
35+
36+
public function isScheme(string $scheme): bool
37+
{
38+
return $this->scheme === $scheme;
39+
}
40+
41+
public function __toString(): string
42+
{
43+
$dsn = $this->scheme.'://';
44+
45+
if ($this->isAuthenticated()) {
46+
$dsn .= rawurlencode($this->user).':'.rawurlencode($this->pass).'@';
47+
}
48+
49+
$dsn .= $this->host;
50+
51+
if (null !== $this->port) {
52+
$dsn .= ':'.$this->port;
53+
}
54+
55+
if (null !== $this->path) {
56+
$dsn .= $this->path;
57+
}
58+
59+
if (null !== $this->query) {
60+
$dsn .= '?'.$this->query;
61+
}
62+
63+
if (null !== $this->fragment) {
64+
$dsn .= '#'.$this->fragment;
65+
}
66+
67+
return $dsn;
68+
}
69+
70+
public function parsedQuery(): array
71+
{
72+
if (null === $this->query) {
73+
return [];
74+
}
75+
76+
parse_str($this->query, $query);
77+
78+
return $query;
79+
}
80+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.