UriResolverTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. declare(strict_types=1);
  3. namespace GuzzleHttp\Tests\Psr7;
  4. use GuzzleHttp\Psr7\Uri;
  5. use GuzzleHttp\Psr7\UriResolver;
  6. use PHPUnit\Framework\TestCase;
  7. use Psr\Http\Message\UriInterface;
  8. /**
  9. * @covers \GuzzleHttp\Psr7\UriResolver
  10. */
  11. class UriResolverTest extends TestCase
  12. {
  13. private const RFC3986_BASE = 'http://a/b/c/d;p?q';
  14. /**
  15. * @dataProvider getResolveTestCases
  16. */
  17. public function testResolveUri(string $base, string $rel, string $expectedTarget): void
  18. {
  19. $baseUri = new Uri($base);
  20. $targetUri = UriResolver::resolve($baseUri, new Uri($rel));
  21. self::assertInstanceOf(UriInterface::class, $targetUri);
  22. self::assertSame($expectedTarget, (string) $targetUri);
  23. // This ensures there are no test cases that only work in the resolve() direction but not the
  24. // opposite via relativize(). This can happen when both base and rel URI are relative-path
  25. // references resulting in another relative-path URI.
  26. self::assertSame($expectedTarget, (string) UriResolver::resolve($baseUri, $targetUri));
  27. }
  28. /**
  29. * @dataProvider getResolveTestCases
  30. */
  31. public function testRelativizeUri(string $base, string $expectedRelativeReference, string $target): void
  32. {
  33. $baseUri = new Uri($base);
  34. $relativeUri = UriResolver::relativize($baseUri, new Uri($target));
  35. self::assertInstanceOf(UriInterface::class, $relativeUri);
  36. // There are test-cases with too many dot-segments and relative references that are equal like "." == "./".
  37. // So apart from the same-as condition, this alternative success condition is necessary.
  38. self::assertTrue(
  39. $expectedRelativeReference === (string) $relativeUri
  40. || $target === (string) UriResolver::resolve($baseUri, $relativeUri),
  41. sprintf(
  42. '"%s" is not the correct relative reference as it does not resolve to the target URI from the base URI',
  43. (string) $relativeUri
  44. )
  45. );
  46. }
  47. /**
  48. * @dataProvider getRelativizeTestCases
  49. */
  50. public function testRelativizeUriWithUniqueTests(string $base, string $target, string $expectedRelativeReference): void
  51. {
  52. $baseUri = new Uri($base);
  53. $targetUri = new Uri($target);
  54. $relativeUri = UriResolver::relativize($baseUri, $targetUri);
  55. self::assertInstanceOf(UriInterface::class, $relativeUri);
  56. self::assertSame($expectedRelativeReference, (string) $relativeUri);
  57. self::assertSame((string) UriResolver::resolve($baseUri, $targetUri), (string) UriResolver::resolve($baseUri, $relativeUri));
  58. }
  59. public function getResolveTestCases(): iterable
  60. {
  61. return [
  62. [self::RFC3986_BASE, 'g:h', 'g:h'],
  63. [self::RFC3986_BASE, 'g', 'http://a/b/c/g'],
  64. [self::RFC3986_BASE, './g', 'http://a/b/c/g'],
  65. [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'],
  66. [self::RFC3986_BASE, '/g', 'http://a/g'],
  67. [self::RFC3986_BASE, '//g', 'http://g'],
  68. [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'],
  69. [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'],
  70. [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'],
  71. [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'],
  72. [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'],
  73. [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'],
  74. [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'],
  75. [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'],
  76. [self::RFC3986_BASE, '', self::RFC3986_BASE],
  77. [self::RFC3986_BASE, '.', 'http://a/b/c/'],
  78. [self::RFC3986_BASE, './', 'http://a/b/c/'],
  79. [self::RFC3986_BASE, '..', 'http://a/b/'],
  80. [self::RFC3986_BASE, '../', 'http://a/b/'],
  81. [self::RFC3986_BASE, '../g', 'http://a/b/g'],
  82. [self::RFC3986_BASE, '../..', 'http://a/'],
  83. [self::RFC3986_BASE, '../../', 'http://a/'],
  84. [self::RFC3986_BASE, '../../g', 'http://a/g'],
  85. [self::RFC3986_BASE, '../../../g', 'http://a/g'],
  86. [self::RFC3986_BASE, '../../../../g', 'http://a/g'],
  87. [self::RFC3986_BASE, '/./g', 'http://a/g'],
  88. [self::RFC3986_BASE, '/../g', 'http://a/g'],
  89. [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'],
  90. [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'],
  91. [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'],
  92. [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'],
  93. [self::RFC3986_BASE, './../g', 'http://a/b/g'],
  94. [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'],
  95. [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'],
  96. [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'],
  97. [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'],
  98. [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
  99. [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
  100. // dot-segments in the query or fragment
  101. [self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'],
  102. [self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'],
  103. [self::RFC3986_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x'],
  104. [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
  105. [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
  106. [self::RFC3986_BASE, '?y#s', 'http://a/b/c/d;p?y#s'],
  107. // base with fragment
  108. ['http://a/b/c?q#s', '?y', 'http://a/b/c?y'],
  109. // base with user info
  110. ['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'],
  111. ['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'],
  112. // path ending with slash or no slash at all
  113. ['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'],
  114. ['urn:no-slash', 'e', 'urn:e'],
  115. // path ending without slash and multi-segment relative part
  116. ['http://a/b/c', 'd/e', 'http://a/b/d/e'],
  117. // falsey relative parts
  118. [self::RFC3986_BASE, '//0', 'http://0'],
  119. [self::RFC3986_BASE, '0', 'http://a/b/c/0'],
  120. [self::RFC3986_BASE, '?0', 'http://a/b/c/d;p?0'],
  121. [self::RFC3986_BASE, '#0', 'http://a/b/c/d;p?q#0'],
  122. // absolute path base URI
  123. ['/a/b/', '', '/a/b/'],
  124. ['/a/b', '', '/a/b'],
  125. ['/', 'a', '/a'],
  126. ['/', 'a/b', '/a/b'],
  127. ['/a/b', 'g', '/a/g'],
  128. ['/a/b/c', './', '/a/b/'],
  129. ['/a/b/', '../', '/a/'],
  130. ['/a/b/c', '../', '/a/'],
  131. ['/a/b/', '../../x/y/z/', '/x/y/z/'],
  132. ['/a/b/c/d/e', '../../../c/d', '/a/c/d'],
  133. ['/a/b/c//', '../', '/a/b/c/'],
  134. ['/a/b/c/', './/', '/a/b/c//'],
  135. ['/a/b/c', '../../../../a', '/a'],
  136. ['/a/b/c', '../../../..', '/'],
  137. // not actually a dot-segment
  138. ['/a/b/c', '..a/b..', '/a/b/..a/b..'],
  139. // '' cannot be used as relative reference as it would inherit the base query component
  140. ['/a/b?q', 'b', '/a/b'],
  141. ['/a/b/?q', './', '/a/b/'],
  142. // path with colon: "with:colon" would be the wrong relative reference
  143. ['/a/', './with:colon', '/a/with:colon'],
  144. ['/a/', 'b/with:colon', '/a/b/with:colon'],
  145. ['/a/', './:b/', '/a/:b/'],
  146. // relative path references
  147. ['a', 'a/b', 'a/b'],
  148. ['', '', ''],
  149. ['', '..', ''],
  150. ['/', '..', '/'],
  151. ['urn:a/b', '..//a/b', 'urn:/a/b'],
  152. // network path references
  153. // empty base path and relative-path reference
  154. ['//example.com', 'a', '//example.com/a'],
  155. // path starting with two slashes
  156. ['//example.com//two-slashes', './', '//example.com//'],
  157. ['//example.com', './/', '//example.com//'],
  158. ['//example.com/', './/', '//example.com//'],
  159. // base URI has less components than relative URI
  160. ['/', '//a/b?q#h', '//a/b?q#h'],
  161. ['/', 'urn:/', 'urn:/'],
  162. ];
  163. }
  164. /**
  165. * Some additional tests to getResolveTestCases() that only make sense for relativize.
  166. */
  167. public function getRelativizeTestCases(): iterable
  168. {
  169. return [
  170. // targets that are relative-path references are returned as-is
  171. ['a/b', 'b/c', 'b/c'],
  172. ['a/b/c', '../b/c', '../b/c'],
  173. ['a', '', ''],
  174. ['a', './', './'],
  175. ['a', 'a/..', 'a/..'],
  176. ['/a/b/?q', '?q#h', '?q#h'],
  177. ['/a/b/?q', '#h', '#h'],
  178. ['/a/b/?q', 'c#h', 'c#h'],
  179. // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
  180. // inherit the base query component when resolving.
  181. ['/a/b/?q', '/a/b/#h', './#h'],
  182. ['/', '/#h', '#h'],
  183. ['/', '/', ''],
  184. ['http://a', 'http://a/', './'],
  185. ['urn:a/b?q', 'urn:x/y?q', '../x/y?q'],
  186. ['urn:', 'urn:/', './/'],
  187. ['urn:a/b?q', 'urn:', '../'],
  188. // target URI has less components than base URI
  189. ['http://a/b/', '//a/b/c', 'c'],
  190. ['http://a/b/', '/b/c', 'c'],
  191. ['http://a/b/', '/x/y', '../x/y'],
  192. ['http://a/b/', '/', '../'],
  193. // absolute target URI without authority but base URI has one
  194. ['urn://a/b/', 'urn:/b/', 'urn:/b/'],
  195. ];
  196. }
  197. }