Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 11 | class UriTest extends TestCase |
||
| 12 | { |
||
| 13 | const RFC3986_BASE = 'http://a/b/c/d;p?q'; |
||
| 14 | |||
| 15 | public function testParsesProvidedUri() |
||
| 16 | { |
||
| 17 | $uri = new Uri('https://user:[email protected]:8080/path/123?q=abc#test'); |
||
| 18 | |||
| 19 | $this->assertSame('https', $uri->getScheme()); |
||
| 20 | $this->assertSame('user:[email protected]:8080', $uri->getAuthority()); |
||
| 21 | $this->assertSame('user:pass', $uri->getUserInfo()); |
||
| 22 | $this->assertSame('example.com', $uri->getHost()); |
||
| 23 | $this->assertSame(8080, $uri->getPort()); |
||
| 24 | $this->assertSame('/path/123', $uri->getPath()); |
||
| 25 | $this->assertSame('q=abc', $uri->getQuery()); |
||
| 26 | $this->assertSame('test', $uri->getFragment()); |
||
| 27 | $this->assertSame('https://user:[email protected]:8080/path/123?q=abc#test', (string) $uri); |
||
| 28 | } |
||
| 29 | |||
| 30 | public function testCanTransformAndRetrievePartsIndividually() |
||
| 31 | { |
||
| 32 | $uri = (new Uri()) |
||
| 33 | ->withScheme('https') |
||
| 34 | ->withUserInfo('user', 'pass') |
||
| 35 | ->withHost('example.com') |
||
| 36 | ->withPort(8080) |
||
| 37 | ->withPath('/path/123') |
||
| 38 | ->withQuery('q=abc') |
||
| 39 | ->withFragment('test'); |
||
| 40 | |||
| 41 | $this->assertSame('https', $uri->getScheme()); |
||
| 42 | $this->assertSame('user:[email protected]:8080', $uri->getAuthority()); |
||
| 43 | $this->assertSame('user:pass', $uri->getUserInfo()); |
||
| 44 | $this->assertSame('example.com', $uri->getHost()); |
||
| 45 | $this->assertSame(8080, $uri->getPort()); |
||
| 46 | $this->assertSame('/path/123', $uri->getPath()); |
||
| 47 | $this->assertSame('q=abc', $uri->getQuery()); |
||
| 48 | $this->assertSame('test', $uri->getFragment()); |
||
| 49 | $this->assertSame('https://user:[email protected]:8080/path/123?q=abc#test', (string) $uri); |
||
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @dataProvider getValidUris |
||
| 54 | */ |
||
| 55 | public function testValidUrisStayValid($input) |
||
| 56 | { |
||
| 57 | $uri = new Uri($input); |
||
| 58 | |||
| 59 | $this->assertSame($input, (string) $uri); |
||
| 60 | } |
||
| 61 | |||
| 62 | public function getValidUris() |
||
| 63 | { |
||
| 64 | return [ |
||
| 65 | ['urn:path-rootless'], |
||
| 66 | ['urn:path:with:colon'], |
||
| 67 | ['urn:/path-absolute'], |
||
| 68 | ['urn:/'], |
||
| 69 | // only scheme with empty path |
||
| 70 | ['urn:'], |
||
| 71 | // only path |
||
| 72 | ['/'], |
||
| 73 | ['relative/'], |
||
| 74 | ['0'], |
||
| 75 | // same document reference |
||
| 76 | [''], |
||
| 77 | // network path without scheme |
||
| 78 | ['//example.org'], |
||
| 79 | ['//example.org/'], |
||
| 80 | ['//example.org?q#h'], |
||
| 81 | // only query |
||
| 82 | ['?q'], |
||
| 83 | ['?q=abc&foo=bar'], |
||
| 84 | // only fragment |
||
| 85 | ['#fragment'], |
||
| 86 | // dot segments are not removed automatically |
||
| 87 | ['./foo/../bar'], |
||
| 88 | ]; |
||
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * @dataProvider getInvalidUris |
||
| 93 | */ |
||
| 94 | public function testInvalidUrisThrowException($invalidUri) |
||
| 95 | { |
||
| 96 | $this->expectException(\InvalidArgumentException::class); |
||
| 97 | $this->expectExceptionMessage('Unable to parse URI'); |
||
| 98 | |||
| 99 | new Uri($invalidUri); |
||
| 100 | } |
||
| 101 | |||
| 102 | public function getInvalidUris() |
||
| 103 | { |
||
| 104 | return [ |
||
| 105 | // parse_url() requires the host component which makes sense for http(s) |
||
| 106 | // but not when the scheme is not known or different. So '//' or '///' is |
||
| 107 | // currently invalid as well but should not according to RFC 3986. |
||
| 108 | ['http://'], |
||
| 109 | ['urn://host:with:colon'], // host cannot contain ":" |
||
| 110 | ]; |
||
| 111 | } |
||
| 112 | |||
| 113 | public function testPortMustBeValid() |
||
| 114 | { |
||
| 115 | $this->expectException(\InvalidArgumentException::class); |
||
| 116 | $this->expectExceptionMessage('Invalid port: 100000. Must be between 1 and 65535'); |
||
| 117 | |||
| 118 | (new Uri())->withPort(100000); |
||
| 119 | } |
||
| 120 | |||
| 121 | public function testWithPortCannotBeZero() |
||
| 122 | { |
||
| 123 | $this->expectException(\InvalidArgumentException::class); |
||
| 124 | $this->expectExceptionMessage('Invalid port: 0. Must be between 1 and 65535'); |
||
| 125 | |||
| 126 | (new Uri())->withPort(0); |
||
| 127 | } |
||
| 128 | |||
| 129 | public function testParseUriPortCannotBeZero() |
||
| 130 | { |
||
| 131 | $this->expectException(\InvalidArgumentException::class); |
||
| 132 | $this->expectExceptionMessage('Unable to parse URI'); |
||
| 133 | |||
| 134 | new Uri('//example.com:0'); |
||
| 135 | } |
||
| 136 | |||
| 137 | public function testSchemeMustHaveCorrectType() |
||
| 138 | { |
||
| 139 | $this->expectException(\InvalidArgumentException::class); |
||
| 140 | $this->expectExceptionMessage('Scheme must be a string'); |
||
| 141 | |||
| 142 | (new Uri())->withScheme([]); |
||
| 143 | } |
||
| 144 | |||
| 145 | public function testHostMustHaveCorrectType() |
||
| 146 | { |
||
| 147 | $this->expectException(\InvalidArgumentException::class); |
||
| 148 | $this->expectExceptionMessage('Host must be a string'); |
||
| 149 | |||
| 150 | (new Uri())->withHost([]); |
||
| 151 | } |
||
| 152 | |||
| 153 | public function testPathMustHaveCorrectType() |
||
| 154 | { |
||
| 155 | $this->expectException(\InvalidArgumentException::class); |
||
| 156 | $this->expectExceptionMessage('Path must be a string'); |
||
| 157 | |||
| 158 | (new Uri())->withPath([]); |
||
| 159 | } |
||
| 160 | |||
| 161 | public function testQueryMustHaveCorrectType() |
||
| 162 | { |
||
| 163 | $this->expectException(\InvalidArgumentException::class); |
||
| 164 | $this->expectExceptionMessage('Query and fragment must be a string'); |
||
| 165 | |||
| 166 | (new Uri())->withQuery([]); |
||
| 167 | } |
||
| 168 | |||
| 169 | public function testFragmentMustHaveCorrectType() |
||
| 170 | { |
||
| 171 | $this->expectException(\InvalidArgumentException::class); |
||
| 172 | $this->expectExceptionMessage('Query and fragment must be a string'); |
||
| 173 | |||
| 174 | (new Uri())->withFragment([]); |
||
| 175 | } |
||
| 176 | |||
| 177 | public function testCanParseFalseyUriParts() |
||
| 178 | { |
||
| 179 | $uri = new Uri('0://0:0@0/0?0#0'); |
||
| 180 | |||
| 181 | $this->assertSame('0', $uri->getScheme()); |
||
| 182 | $this->assertSame('0:0@0', $uri->getAuthority()); |
||
| 183 | $this->assertSame('0:0', $uri->getUserInfo()); |
||
| 184 | $this->assertSame('0', $uri->getHost()); |
||
| 185 | $this->assertSame('/0', $uri->getPath()); |
||
| 186 | $this->assertSame('0', $uri->getQuery()); |
||
| 187 | $this->assertSame('0', $uri->getFragment()); |
||
| 188 | $this->assertSame('0://0:0@0/0?0#0', (string) $uri); |
||
| 189 | } |
||
| 190 | |||
| 191 | public function testCanConstructFalseyUriParts() |
||
| 192 | { |
||
| 193 | $uri = (new Uri()) |
||
| 194 | ->withScheme('0') |
||
| 195 | ->withUserInfo('0', '0') |
||
| 196 | ->withHost('0') |
||
| 197 | ->withPath('/0') |
||
| 198 | ->withQuery('0') |
||
| 199 | ->withFragment('0'); |
||
| 200 | |||
| 201 | $this->assertSame('0', $uri->getScheme()); |
||
| 202 | $this->assertSame('0:0@0', $uri->getAuthority()); |
||
| 203 | $this->assertSame('0:0', $uri->getUserInfo()); |
||
| 204 | $this->assertSame('0', $uri->getHost()); |
||
| 205 | $this->assertSame('/0', $uri->getPath()); |
||
| 206 | $this->assertSame('0', $uri->getQuery()); |
||
| 207 | $this->assertSame('0', $uri->getFragment()); |
||
| 208 | $this->assertSame('0://0:0@0/0?0#0', (string) $uri); |
||
| 209 | } |
||
| 210 | |||
| 211 | public function getResolveTestCases() |
||
| 212 | { |
||
| 213 | return [ |
||
| 214 | [self::RFC3986_BASE, 'g:h', 'g:h'], |
||
| 215 | [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], |
||
| 216 | [self::RFC3986_BASE, './g', 'http://a/b/c/g'], |
||
| 217 | [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], |
||
| 218 | [self::RFC3986_BASE, '/g', 'http://a/g'], |
||
| 219 | [self::RFC3986_BASE, '//g', 'http://g'], |
||
| 220 | [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], |
||
| 221 | [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], |
||
| 222 | [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], |
||
| 223 | [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], |
||
| 224 | [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], |
||
| 225 | [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], |
||
| 226 | [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], |
||
| 227 | [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], |
||
| 228 | [self::RFC3986_BASE, '', self::RFC3986_BASE], |
||
| 229 | [self::RFC3986_BASE, '.', 'http://a/b/c/'], |
||
| 230 | [self::RFC3986_BASE, './', 'http://a/b/c/'], |
||
| 231 | [self::RFC3986_BASE, '..', 'http://a/b/'], |
||
| 232 | [self::RFC3986_BASE, '../', 'http://a/b/'], |
||
| 233 | [self::RFC3986_BASE, '../g', 'http://a/b/g'], |
||
| 234 | [self::RFC3986_BASE, '../..', 'http://a/'], |
||
| 235 | [self::RFC3986_BASE, '../../', 'http://a/'], |
||
| 236 | [self::RFC3986_BASE, '../../g', 'http://a/g'], |
||
| 237 | [self::RFC3986_BASE, '../../../g', 'http://a/g'], |
||
| 238 | [self::RFC3986_BASE, '../../../../g', 'http://a/g'], |
||
| 239 | [self::RFC3986_BASE, '/./g', 'http://a/g'], |
||
| 240 | [self::RFC3986_BASE, '/../g', 'http://a/g'], |
||
| 241 | [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], |
||
| 242 | [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], |
||
| 243 | [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], |
||
| 244 | [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], |
||
| 245 | [self::RFC3986_BASE, './../g', 'http://a/b/g'], |
||
| 246 | [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], |
||
| 247 | [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], |
||
| 248 | [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], |
||
| 249 | [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], |
||
| 250 | [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], |
||
| 251 | [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], |
||
| 252 | // dot-segments in the query or fragment |
||
| 253 | [self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'], |
||
| 254 | [self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'], |
||
| 255 | [self::RFC3986_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x'], |
||
| 256 | [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'], |
||
| 257 | [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'], |
||
| 258 | [self::RFC3986_BASE, '?y#s', 'http://a/b/c/d;p?y#s'], |
||
| 259 | ['http://a/b/c/d;p?q#s', '?y', 'http://a/b/c/d;p?y'], |
||
| 260 | ['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'], |
||
| 261 | ['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'], |
||
| 262 | ['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'], |
||
| 263 | ['urn:no-slash', 'e', 'urn:e'], |
||
| 264 | // falsey relative parts |
||
| 265 | [self::RFC3986_BASE, '//0', 'http://0'], |
||
| 266 | [self::RFC3986_BASE, '0', 'http://a/b/c/0'], |
||
| 267 | [self::RFC3986_BASE, '?0', 'http://a/b/c/d;p?0'], |
||
| 268 | [self::RFC3986_BASE, '#0', 'http://a/b/c/d;p?q#0'], |
||
| 269 | ]; |
||
| 270 | } |
||
| 271 | |||
| 272 | public function testSchemeIsNormalizedToLowercase() |
||
| 273 | { |
||
| 274 | $uri = new Uri('HTTP://example.com'); |
||
| 275 | |||
| 276 | $this->assertSame('http', $uri->getScheme()); |
||
| 277 | $this->assertSame('http://example.com', (string) $uri); |
||
| 278 | |||
| 279 | $uri = (new Uri('//example.com'))->withScheme('HTTP'); |
||
| 280 | |||
| 281 | $this->assertSame('http', $uri->getScheme()); |
||
| 282 | $this->assertSame('http://example.com', (string) $uri); |
||
| 283 | } |
||
| 284 | |||
| 285 | public function testHostIsNormalizedToLowercase() |
||
| 286 | { |
||
| 287 | $uri = new Uri('//eXaMpLe.CoM'); |
||
| 288 | |||
| 289 | $this->assertSame('example.com', $uri->getHost()); |
||
| 290 | $this->assertSame('//example.com', (string) $uri); |
||
| 291 | |||
| 292 | $uri = (new Uri())->withHost('eXaMpLe.CoM'); |
||
| 293 | |||
| 294 | $this->assertSame('example.com', $uri->getHost()); |
||
| 295 | $this->assertSame('//example.com', (string) $uri); |
||
| 296 | } |
||
| 297 | |||
| 298 | public function testPortIsNullIfStandardPortForScheme() |
||
| 299 | { |
||
| 300 | // HTTPS standard port |
||
| 301 | $uri = new Uri('https://example.com:443'); |
||
| 302 | $this->assertNull($uri->getPort()); |
||
| 303 | $this->assertSame('example.com', $uri->getAuthority()); |
||
| 304 | |||
| 305 | $uri = (new Uri('https://example.com'))->withPort(443); |
||
| 306 | $this->assertNull($uri->getPort()); |
||
| 307 | $this->assertSame('example.com', $uri->getAuthority()); |
||
| 308 | |||
| 309 | // HTTP standard port |
||
| 310 | $uri = new Uri('http://example.com:80'); |
||
| 311 | $this->assertNull($uri->getPort()); |
||
| 312 | $this->assertSame('example.com', $uri->getAuthority()); |
||
| 313 | |||
| 314 | $uri = (new Uri('http://example.com'))->withPort(80); |
||
| 315 | $this->assertNull($uri->getPort()); |
||
| 316 | $this->assertSame('example.com', $uri->getAuthority()); |
||
| 317 | } |
||
| 318 | |||
| 319 | public function testPortIsReturnedIfSchemeUnknown() |
||
| 320 | { |
||
| 321 | $uri = (new Uri('//example.com'))->withPort(80); |
||
| 322 | |||
| 323 | $this->assertSame(80, $uri->getPort()); |
||
| 324 | $this->assertSame('example.com:80', $uri->getAuthority()); |
||
| 325 | } |
||
| 326 | |||
| 327 | public function testStandardPortIsNullIfSchemeChanges() |
||
| 328 | { |
||
| 329 | $uri = new Uri('http://example.com:443'); |
||
| 330 | $this->assertSame('http', $uri->getScheme()); |
||
| 331 | $this->assertSame(443, $uri->getPort()); |
||
| 332 | |||
| 333 | $uri = $uri->withScheme('https'); |
||
| 334 | $this->assertNull($uri->getPort()); |
||
| 335 | } |
||
| 336 | |||
| 337 | public function testPortPassedAsStringIsCastedToInt() |
||
| 338 | { |
||
| 339 | $uri = (new Uri('//example.com'))->withPort('8080'); |
||
| 340 | |||
| 341 | $this->assertSame(8080, $uri->getPort(), 'Port is returned as integer'); |
||
| 342 | $this->assertSame('example.com:8080', $uri->getAuthority()); |
||
| 343 | } |
||
| 344 | |||
| 345 | public function testPortCanBeRemoved() |
||
| 346 | { |
||
| 347 | $uri = (new Uri('http://example.com:8080'))->withPort(null); |
||
| 348 | |||
| 349 | $this->assertNull($uri->getPort()); |
||
| 350 | $this->assertSame('http://example.com', (string) $uri); |
||
| 351 | } |
||
| 352 | |||
| 353 | public function testAuthorityWithUserInfoButWithoutHost() |
||
| 354 | { |
||
| 355 | $uri = (new Uri())->withUserInfo('user', 'pass'); |
||
| 356 | |||
| 357 | $this->assertSame('user:pass', $uri->getUserInfo()); |
||
| 358 | $this->assertSame('', $uri->getAuthority()); |
||
| 359 | } |
||
| 360 | |||
| 361 | public function uriComponentsEncodingProvider() |
||
| 362 | { |
||
| 363 | $unreserved = 'a-zA-Z0-9.-_~!$&\'()*+,;=:@'; |
||
| 364 | |||
| 365 | return [ |
||
| 366 | // Percent encode spaces |
||
| 367 | ['/pa th?q=va lue#frag ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'], |
||
| 368 | // Percent encode multibyte |
||
| 369 | ['/€?€#€', '/%E2%82%AC', '%E2%82%AC', '%E2%82%AC', '/%E2%82%AC?%E2%82%AC#%E2%82%AC'], |
||
| 370 | // Don't encode something that's already encoded |
||
| 371 | ['/pa%20th?q=va%20lue#frag%20ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'], |
||
| 372 | // Percent encode invalid percent encodings |
||
| 373 | ['/pa%2-th?q=va%2-lue#frag%2-ment', '/pa%252-th', 'q=va%252-lue', 'frag%252-ment', '/pa%252-th?q=va%252-lue#frag%252-ment'], |
||
| 374 | // Don't encode path segments |
||
| 375 | ['/pa/th//two?q=va/lue#frag/ment', '/pa/th//two', 'q=va/lue', 'frag/ment', '/pa/th//two?q=va/lue#frag/ment'], |
||
| 376 | // Don't encode unreserved chars or sub-delimiters |
||
| 377 | ["/$unreserved?$unreserved#$unreserved", "/$unreserved", $unreserved, $unreserved, "/$unreserved?$unreserved#$unreserved"], |
||
| 378 | // Encoded unreserved chars are not decoded |
||
| 379 | ['/p%61th?q=v%61lue#fr%61gment', '/p%61th', 'q=v%61lue', 'fr%61gment', '/p%61th?q=v%61lue#fr%61gment'], |
||
| 380 | ]; |
||
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * @dataProvider uriComponentsEncodingProvider |
||
| 385 | */ |
||
| 386 | public function testUriComponentsGetEncodedProperly($input, $path, $query, $fragment, $output) |
||
| 387 | { |
||
| 388 | $uri = new Uri($input); |
||
| 389 | $this->assertSame($path, $uri->getPath()); |
||
| 390 | $this->assertSame($query, $uri->getQuery()); |
||
| 391 | $this->assertSame($fragment, $uri->getFragment()); |
||
| 392 | $this->assertSame($output, (string) $uri); |
||
| 393 | } |
||
| 394 | |||
| 395 | public function testWithPathEncodesProperly() |
||
| 396 | { |
||
| 397 | $uri = (new Uri())->withPath('/baz?#€/b%61r'); |
||
| 398 | // Query and fragment delimiters and multibyte chars are encoded. |
||
| 399 | $this->assertSame('/baz%3F%23%E2%82%AC/b%61r', $uri->getPath()); |
||
| 400 | $this->assertSame('/baz%3F%23%E2%82%AC/b%61r', (string) $uri); |
||
| 401 | } |
||
| 402 | |||
| 403 | public function testWithQueryEncodesProperly() |
||
| 404 | { |
||
| 405 | $uri = (new Uri())->withQuery('?=#&€=/&b%61r'); |
||
| 406 | // A query starting with a "?" is valid and must not be magically removed. Otherwise it would be impossible to |
||
| 407 | // construct such an URI. Also the "?" and "/" does not need to be encoded in the query. |
||
| 408 | $this->assertSame('?=%23&%E2%82%AC=/&b%61r', $uri->getQuery()); |
||
| 409 | $this->assertSame('??=%23&%E2%82%AC=/&b%61r', (string) $uri); |
||
| 410 | } |
||
| 411 | |||
| 412 | public function testWithFragmentEncodesProperly() |
||
| 413 | { |
||
| 414 | $uri = (new Uri())->withFragment('#€?/b%61r'); |
||
| 415 | // A fragment starting with a "#" is valid and must not be magically removed. Otherwise it would be impossible to |
||
| 416 | // construct such an URI. Also the "?" and "/" does not need to be encoded in the fragment. |
||
| 417 | $this->assertSame('%23%E2%82%AC?/b%61r', $uri->getFragment()); |
||
| 418 | $this->assertSame('#%23%E2%82%AC?/b%61r', (string) $uri); |
||
| 419 | } |
||
| 420 | |||
| 421 | public function testAllowsForRelativeUri() |
||
| 422 | { |
||
| 423 | $uri = (new Uri())->withPath('foo'); |
||
| 424 | $this->assertSame('foo', $uri->getPath()); |
||
| 425 | $this->assertSame('foo', (string) $uri); |
||
| 426 | } |
||
| 427 | |||
| 428 | public function testAddsSlashForRelativeUriStringWithHost() |
||
| 429 | { |
||
| 430 | // If the path is rootless and an authority is present, the path MUST |
||
| 431 | // be prefixed by "/". |
||
| 432 | $uri = (new Uri())->withPath('foo')->withHost('example.com'); |
||
| 433 | $this->assertSame('foo', $uri->getPath()); |
||
| 434 | // concatenating a relative path with a host doesn't work: "//example.comfoo" would be wrong |
||
| 435 | $this->assertSame('//example.com/foo', (string) $uri); |
||
| 436 | } |
||
| 437 | |||
| 438 | public function testRemoveExtraSlashesWihoutHost() |
||
| 439 | { |
||
| 440 | // If the path is starting with more than one "/" and no authority is |
||
| 441 | // present, the starting slashes MUST be reduced to one. |
||
| 442 | $uri = (new Uri())->withPath('//foo'); |
||
| 443 | $this->assertSame('//foo', $uri->getPath()); |
||
| 444 | // URI "//foo" would be interpreted as network reference and thus change the original path to the host |
||
| 445 | $this->assertSame('/foo', (string) $uri); |
||
| 446 | } |
||
| 447 | |||
| 448 | public function testDefaultReturnValuesOfGetters() |
||
| 449 | { |
||
| 450 | $uri = new Uri(); |
||
| 451 | |||
| 452 | $this->assertSame('', $uri->getScheme()); |
||
| 453 | $this->assertSame('', $uri->getAuthority()); |
||
| 454 | $this->assertSame('', $uri->getUserInfo()); |
||
| 455 | $this->assertSame('', $uri->getHost()); |
||
| 456 | $this->assertNull($uri->getPort()); |
||
| 457 | $this->assertSame('', $uri->getPath()); |
||
| 458 | $this->assertSame('', $uri->getQuery()); |
||
| 459 | $this->assertSame('', $uri->getFragment()); |
||
| 460 | } |
||
| 461 | |||
| 462 | public function testImmutability() |
||
| 463 | { |
||
| 464 | $uri = new Uri(); |
||
| 465 | |||
| 466 | $this->assertNotSame($uri, $uri->withScheme('https')); |
||
| 467 | $this->assertNotSame($uri, $uri->withUserInfo('user', 'pass')); |
||
| 468 | $this->assertNotSame($uri, $uri->withHost('example.com')); |
||
| 469 | $this->assertNotSame($uri, $uri->withPort(8080)); |
||
| 470 | $this->assertNotSame($uri, $uri->withPath('/path/123')); |
||
| 471 | $this->assertNotSame($uri, $uri->withQuery('q=abc')); |
||
| 472 | $this->assertNotSame($uri, $uri->withFragment('test')); |
||
| 473 | } |
||
| 474 | } |
||
| 475 |