Skip to content

Commit 4dec6d8

Browse files
committed
Merge pull request #15 from SenseException/url-class
New class Url as addition for a later Puli PR (2nd attempt)
2 parents 56e08b0 + 09e2df5 commit 4dec6d8

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ Path::getHomeDirectory();
8686
// => /home/webmozart
8787
```
8888

89+
```php
90+
use Webmozart\PathUtil\Url;
91+
92+
echo Url::makeRelative('http://example.com/webmozart/css/style.css', 'http://example.com/webmozart/puli');
93+
// => ../css/style.css
94+
```
95+
8996
Learn more in the [Documentation] and the [API Docs].
9097

9198
Authors

src/Url.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the webmozart/path-util package.
5+
*
6+
* (c) Bernhard Schussek <bschussek@gmail.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 Webmozart\PathUtil;
13+
14+
use InvalidArgumentException;
15+
use Webmozart\Assert\Assert;
16+
17+
/**
18+
* Contains utility methods for handling URL strings.
19+
*
20+
* The methods in this class are able to deal with URLs.
21+
*
22+
* @since 2.3
23+
*
24+
* @author Bernhard Schussek <bschussek@gmail.com>
25+
* @author Claudio Zizza <claudio@budgegeria.de>
26+
*/
27+
final class Url
28+
{
29+
/**
30+
* Turns a URL into a relative path.
31+
*
32+
* The result is a canonical path. This class is using functionality of Path class.
33+
*
34+
* @see Path
35+
*
36+
* @param string $url A URL to make relative.
37+
* @param string $baseUrl A base URL.
38+
*
39+
* @return string
40+
*
41+
* @throws InvalidArgumentException If the URL and base URL does
42+
* not match.
43+
*/
44+
public static function makeRelative($url, $baseUrl)
45+
{
46+
Assert::string($url, 'The URL must be a string. Got: %s');
47+
Assert::string($baseUrl, 'The base URL must be a string. Got: %s');
48+
Assert::contains($baseUrl, '://', '%s is not an absolute Url.');
49+
50+
list($baseHost, $basePath) = self::split($baseUrl);
51+
52+
if (false === strpos($url, '://')) {
53+
if (0 === strpos($url, '/')) {
54+
$host = $baseHost;
55+
} else {
56+
$host = '';
57+
}
58+
$path = $url;
59+
} else {
60+
list($host, $path) = self::split($url);
61+
}
62+
63+
if ('' !== $host && $host !== $baseHost) {
64+
throw new InvalidArgumentException(sprintf(
65+
'The URL "%s" cannot be made relative to "%s" since their host names are different.',
66+
$host,
67+
$baseHost
68+
));
69+
}
70+
71+
return Path::makeRelative($path, $basePath);
72+
}
73+
74+
/**
75+
* Splits a URL into its host and the path.
76+
*
77+
* ```php
78+
* list ($root, $path) = Path::split("http://example.com/webmozart")
79+
* // => array("http://example.com", "/webmozart")
80+
*
81+
* list ($root, $path) = Path::split("http://example.com")
82+
* // => array("http://example.com", "")
83+
* ```
84+
*
85+
* @param string $url The URL to split.
86+
*
87+
* @return string[] An array with the host and the path of the URL.
88+
*
89+
* @throws InvalidArgumentException If $url is not a URL.
90+
*/
91+
private static function split($url)
92+
{
93+
$pos = strpos($url, '://');
94+
$scheme = substr($url, 0, $pos + 3);
95+
$url = substr($url, $pos + 3);
96+
97+
if (false !== ($pos = strpos($url, '/'))) {
98+
$host = substr($url, 0, $pos);
99+
$url = substr($url, $pos);
100+
} else {
101+
// No path, only host
102+
$host = $url;
103+
$url = '/';
104+
}
105+
106+
// At this point, we have $scheme, $host and $path
107+
$root = $scheme.$host;
108+
109+
return array($root, $url);
110+
}
111+
}

tests/UrlTest.php

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the webmozart/path-util package.
5+
*
6+
* (c) Bernhard Schussek <bschussek@gmail.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 Webmozart\PathUtil\Tests;
13+
14+
use Webmozart\PathUtil\Url;
15+
16+
/**
17+
* @since 2.3
18+
*
19+
* @author Bernhard Schussek <bschussek@gmail.com>
20+
* @author Claudio Zizza <claudio@budgegeria.de>
21+
*/
22+
class UrlTest extends \PHPUnit_Framework_TestCase
23+
{
24+
/**
25+
* @dataProvider provideMakeRelativeTests
26+
* @covers Webmozart\PathUtil\Url
27+
*/
28+
public function testMakeRelative($absolutePath, $basePath, $relativePath)
29+
{
30+
$host = 'http://example.com';
31+
32+
$relative = Url::makeRelative($host.$absolutePath, $host.$basePath);
33+
$this->assertSame($relativePath, $relative);
34+
$relative = Url::makeRelative($absolutePath, $host.$basePath);
35+
$this->assertSame($relativePath, $relative);
36+
}
37+
38+
/**
39+
* @dataProvider provideMakeRelativeIsAlreadyRelativeTests
40+
* @covers Webmozart\PathUtil\Url
41+
*/
42+
public function testMakeRelativeIsAlreadyRelative($absolutePath, $basePath, $relativePath)
43+
{
44+
$host = 'http://example.com';
45+
46+
$relative = Url::makeRelative($absolutePath, $host.$basePath);
47+
$this->assertSame($relativePath, $relative);
48+
}
49+
50+
/**
51+
* @dataProvider provideMakeRelativeTests
52+
* @covers Webmozart\PathUtil\Url
53+
*/
54+
public function testMakeRelativeWithFullUrl($absolutePath, $basePath, $relativePath)
55+
{
56+
$host = 'ftp://user:password@example.com:8080';
57+
58+
$relative = Url::makeRelative($host.$absolutePath, $host.$basePath);
59+
$this->assertSame($relativePath, $relative);
60+
}
61+
62+
/**
63+
* @expectedException \InvalidArgumentException
64+
* @expectedExceptionMessage The URL must be a string. Got: array
65+
* @covers Webmozart\PathUtil\Url
66+
*/
67+
public function testMakeRelativeFailsIfInvalidUrl()
68+
{
69+
Url::makeRelative(array(), 'http://example.com/webmozart/puli');
70+
}
71+
72+
/**
73+
* @expectedException \InvalidArgumentException
74+
* @expectedExceptionMessage The base URL must be a string. Got: array
75+
* @covers Webmozart\PathUtil\Url
76+
*/
77+
public function testMakeRelativeFailsIfInvalidBaseUrl()
78+
{
79+
Url::makeRelative('http://example.com/webmozart/puli/css/style.css', array());
80+
}
81+
82+
/**
83+
* @expectedException \InvalidArgumentException
84+
* @expectedExceptionMessage "webmozart/puli" is not an absolute Url.
85+
* @covers Webmozart\PathUtil\Url
86+
*/
87+
public function testMakeRelativeFailsIfBaseUrlNoUrl()
88+
{
89+
Url::makeRelative('http://example.com/webmozart/puli/css/style.css', 'webmozart/puli');
90+
}
91+
92+
/**
93+
* @expectedException \InvalidArgumentException
94+
* @expectedExceptionMessage "" is not an absolute Url.
95+
* @covers Webmozart\PathUtil\Url
96+
*/
97+
public function testMakeRelativeFailsIfBaseUrlEmpty()
98+
{
99+
Url::makeRelative('http://example.com/webmozart/puli/css/style.css', '');
100+
}
101+
102+
/**
103+
* @expectedException \InvalidArgumentException
104+
* @expectedExceptionMessage The base URL must be a string. Got: NULL
105+
* @covers Webmozart\PathUtil\Url
106+
*/
107+
public function testMakeRelativeFailsIfBaseUrlNull()
108+
{
109+
Url::makeRelative('http://example.com/webmozart/puli/css/style.css', null);
110+
}
111+
112+
/**
113+
* @expectedException \InvalidArgumentException
114+
* @expectedExceptionMessage The URL "http://example.com" cannot be made relative to "http://example2.com" since
115+
* their host names are different.
116+
* @covers Webmozart\PathUtil\Url
117+
*/
118+
public function testMakeRelativeFailsIfDifferentDomains()
119+
{
120+
Url::makeRelative('http://example.com/webmozart/puli/css/style.css', 'http://example2.com/webmozart/puli');
121+
}
122+
123+
public function provideMakeRelativeTests()
124+
{
125+
return array(
126+
127+
array('/webmozart/puli/css/style.css', '/webmozart/puli', 'css/style.css'),
128+
array('/webmozart/puli/css/style.css?key=value&key2=value', '/webmozart/puli', 'css/style.css?key=value&key2=value'),
129+
array('/webmozart/puli/css/style.css?key[]=value&key[]=value', '/webmozart/puli', 'css/style.css?key[]=value&key[]=value'),
130+
array('/webmozart/css/style.css', '/webmozart/puli', '../css/style.css'),
131+
array('/css/style.css', '/webmozart/puli', '../../css/style.css'),
132+
array('/', '/', ''),
133+
134+
// relative to root
135+
array('/css/style.css', '/', 'css/style.css'),
136+
137+
// same sub directories in different base directories
138+
array('/puli/css/style.css', '/webmozart/css', '../../puli/css/style.css'),
139+
140+
array('/webmozart/puli/./css/style.css', '/webmozart/puli', 'css/style.css'),
141+
array('/webmozart/puli/../css/style.css', '/webmozart/puli', '../css/style.css'),
142+
array('/webmozart/puli/.././css/style.css', '/webmozart/puli', '../css/style.css'),
143+
array('/webmozart/puli/./../css/style.css', '/webmozart/puli', '../css/style.css'),
144+
array('/webmozart/puli/../../css/style.css', '/webmozart/puli', '../../css/style.css'),
145+
array('/webmozart/puli/css/style.css', '/webmozart/./puli', 'css/style.css'),
146+
array('/webmozart/puli/css/style.css', '/webmozart/../puli', '../webmozart/puli/css/style.css'),
147+
array('/webmozart/puli/css/style.css', '/webmozart/./../puli', '../webmozart/puli/css/style.css'),
148+
array('/webmozart/puli/css/style.css', '/webmozart/.././puli', '../webmozart/puli/css/style.css'),
149+
array('/webmozart/puli/css/style.css', '/webmozart/../../puli', '../webmozart/puli/css/style.css'),
150+
151+
// first argument shorter than second
152+
array('/css', '/webmozart/puli', '../../css'),
153+
154+
// second argument shorter than first
155+
array('/webmozart/puli', '/css', '../webmozart/puli'),
156+
157+
array('', '', ''),
158+
);
159+
}
160+
161+
public function provideMakeRelativeIsAlreadyRelativeTests()
162+
{
163+
return array(
164+
array('css/style.css', '/webmozart/puli', 'css/style.css'),
165+
array('css/style.css', '', 'css/style.css'),
166+
array('css/../style.css', '', 'style.css'),
167+
array('css/./style.css', '', 'css/style.css'),
168+
array('../style.css', '/', 'style.css'),
169+
array('./style.css', '/', 'style.css'),
170+
array('../../style.css', '/', 'style.css'),
171+
array('../../style.css', '', 'style.css'),
172+
array('./style.css', '', 'style.css'),
173+
array('../style.css', '', 'style.css'),
174+
array('./../style.css', '', 'style.css'),
175+
array('css/./../style.css', '', 'style.css'),
176+
array('css//style.css', '', 'css/style.css'),
177+
);
178+
}
179+
}

0 commit comments

Comments
 (0)