URLTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. <?php
  2. /**
  3. * Tests URL
  4. *
  5. * @group ko7
  6. * @group ko7.core
  7. * @group ko7.core.url
  8. *
  9. * @package KO7
  10. * @category Tests
  11. *
  12. * @author BRMatt <matthew@sigswitch.com>
  13. * @copyright (c) 2007-2016 Kohana Team
  14. * @copyright (c) since 2016 Koseven Team
  15. * @license https://koseven.dev/LICENSE
  16. */
  17. class KO7_URLTest extends Unittest_TestCase
  18. {
  19. /**
  20. * Sets up the environment
  21. */
  22. // @codingStandardsIgnoreStart
  23. public function setUp(): void
  24. // @codingStandardsIgnoreEnd
  25. {
  26. parent::setUp();
  27. KO7::$config->load('url')->set(
  28. 'trusted_hosts',
  29. ['www\.example\.com', 'sub\.example\.com', 'example\.com', 'example\.org']
  30. );
  31. }
  32. /**
  33. * Default values for the environment, see setEnvironment
  34. * @var array
  35. */
  36. // @codingStandardsIgnoreStart
  37. protected $environmentDefault = [
  38. 'KO7::$base_url' => '/ko7/',
  39. 'KO7::$index_file'=> 'index.php',
  40. 'HTTP_HOST' => 'example.com',
  41. '_GET' => [],
  42. ];
  43. // @codingStandardsIgnoreEnd
  44. /**
  45. * Provides test data for test_base()
  46. *
  47. * @return array
  48. */
  49. public function provider_base()
  50. {
  51. return [
  52. // $protocol, $index, $subdomain, $expected, $enviroment
  53. // Test with different combinations of parameters for max code coverage
  54. [NULL, FALSE, NULL, NULL, ['KO7::$base_url' => NULL]],
  55. [NULL, FALSE, NULL, '/', ['KO7::$base_url' => '/']],
  56. [NULL, FALSE, NULL, '/ko7/'],
  57. ['http', FALSE, NULL, 'http://example.com/ko7/'],
  58. ['http', FALSE, 'sub', 'http://sub.example.com/ko7/'],
  59. ['http', FALSE, 'sub', 'http://sub.example.com/ko7/', ['HTTP_HOST' => 'sub.example.com']],
  60. ['http', FALSE, 'sub', 'http://sub.example.com/ko7/', ['HTTP_HOST' => 'invalid.example.com']],
  61. [NULL, TRUE, NULL, '/ko7/index.php/'],
  62. ['http', TRUE, NULL, 'http://example.com/ko7/index.php/'],
  63. ['http', TRUE, 'sub', 'http://sub.example.com/ko7/index.php/'],
  64. ['https', TRUE, NULL, 'https://example.com/ko7/index.php/'],
  65. ['https', TRUE, 'sub', 'https://sub.example.com/ko7/index.php/'],
  66. ['ftp', TRUE, NULL, 'ftp://example.com/ko7/index.php/'],
  67. ['ftp', TRUE, 'sub', 'ftp://sub.example.com/ko7/index.php/'],
  68. // Test for automatic protocol detection, protocol = TRUE
  69. [TRUE, TRUE, NULL, 'cli://example.com/ko7/index.php/', ['HTTPS' => FALSE, 'Request::$initial' => Request::factory('/')->protocol('cli')]],
  70. // Change base url'
  71. ['https', FALSE, NULL, 'https://example.com/ko7/', ['KO7::$base_url' => 'omglol://example.com/ko7/']],
  72. // Use port in base url, issue #3307
  73. ['http', FALSE, NULL, 'http://example.com:8080/', ['KO7::$base_url' => 'example.com:8080/']],
  74. // Use protocol from base url if none specified
  75. [NULL, FALSE, NULL, 'http://www.example.com/', ['KO7::$base_url' => 'http://www.example.com/']],
  76. [NULL, FALSE, 'sub', 'http://sub.example.com/', ['KO7::$base_url' => 'http://www.example.com/']],
  77. // Use HTTP_HOST before SERVER_NAME
  78. ['http', FALSE, NULL, 'http://example.com/ko7/', ['HTTP_HOST' => 'example.com', 'SERVER_NAME' => 'example.org']],
  79. // Use SERVER_NAME if HTTP_HOST DNX
  80. ['http', FALSE, NULL, 'http://example.org/ko7/', ['HTTP_HOST' => NULL, 'SERVER_NAME' => 'example.org']],
  81. ];
  82. }
  83. /**
  84. * Tests URL::base()
  85. *
  86. * @test
  87. * @dataProvider provider_base
  88. * @param mixed $protocol Parameter for Url::base()
  89. * @param boolean $index Parameter for Url::base()
  90. * @param string $subdomain Parameter for Url::base()
  91. * @param string $expected Expected url
  92. * @param array $enviroment Array of enviroment vars to change @see KO7_URLTest::setEnvironment()
  93. */
  94. public function test_base($protocol, $index, $subdomain, $expected, array $enviroment = [])
  95. {
  96. $this->setEnvironment($enviroment);
  97. $this->assertSame(
  98. $expected,
  99. URL::base($protocol, $index, $subdomain)
  100. );
  101. }
  102. /**
  103. * Provides test data for test_site()
  104. *
  105. * @return array
  106. */
  107. public function provider_site()
  108. {
  109. return [
  110. ['', NULL, '/ko7/index.php/'],
  111. ['', 'http', 'http://example.com/ko7/index.php/'],
  112. ['my/site', NULL, '/ko7/index.php/my/site'],
  113. ['my/site', 'http', 'http://example.com/ko7/index.php/my/site'],
  114. // @ticket #3110
  115. ['my/site/page:5', NULL, '/ko7/index.php/my/site/page:5'],
  116. ['my/site/page:5', 'http', 'http://example.com/ko7/index.php/my/site/page:5'],
  117. ['my/site?var=asd&ko7=awesome', NULL, '/ko7/index.php/my/site?var=asd&ko7=awesome'],
  118. ['my/site?var=asd&ko7=awesome', 'http', 'http://example.com/ko7/index.php/my/site?var=asd&ko7=awesome'],
  119. ['?ko7=awesome&life=good', NULL, '/ko7/index.php/?ko7=awesome&life=good'],
  120. ['?ko7=awesome&life=good', 'http', 'http://example.com/ko7/index.php/?ko7=awesome&life=good'],
  121. ['?ko7=awesome&life=good#fact', NULL, '/ko7/index.php/?ko7=awesome&life=good#fact'],
  122. ['?ko7=awesome&life=good#fact', 'http', 'http://example.com/ko7/index.php/?ko7=awesome&life=good#fact'],
  123. ['some/long/route/goes/here?ko7=awesome&life=good#fact', NULL, '/ko7/index.php/some/long/route/goes/here?ko7=awesome&life=good#fact'],
  124. ['some/long/route/goes/here?ko7=awesome&life=good#fact', 'http', 'http://example.com/ko7/index.php/some/long/route/goes/here?ko7=awesome&life=good#fact'],
  125. ['/route/goes/here?ko7=awesome&life=good#fact', 'https', 'https://example.com/ko7/index.php/route/goes/here?ko7=awesome&life=good#fact'],
  126. ['/route/goes/here?ko7=awesome&life=good#fact', 'ftp', 'ftp://example.com/ko7/index.php/route/goes/here?ko7=awesome&life=good#fact'],
  127. ];
  128. }
  129. /**
  130. * Tests URL::site()
  131. *
  132. * @test
  133. * @dataProvider provider_site
  134. * @param string $uri URI to use
  135. * @param boolean|string $protocol Protocol to use
  136. * @param string $expected Expected result
  137. * @param array $enviroment Array of enviroment vars to set
  138. */
  139. public function test_site($uri, $protocol, $expected, array $enviroment = [])
  140. {
  141. $this->setEnvironment($enviroment);
  142. $this->assertSame(
  143. $expected,
  144. URL::site($uri, $protocol)
  145. );
  146. }
  147. /**
  148. * Provides test data for test_site_url_encode_uri()
  149. * See issue #2680
  150. *
  151. * @return array
  152. */
  153. public function provider_site_url_encode_uri()
  154. {
  155. $provider = [
  156. ['test', 'encode'],
  157. ['test', 'éñçø∂ë∂'],
  158. ['†é߆', 'encode'],
  159. ['†é߆', 'éñçø∂ë∂', 'µåñ¥'],
  160. ];
  161. foreach ($provider as $i => $params)
  162. {
  163. // Every non-ASCII character except for forward slash should be encoded...
  164. $expected = implode('/', array_map('rawurlencode', $params));
  165. // ... from a URI that is not encoded
  166. $uri = implode('/', $params);
  167. $provider[$i] = ["/ko7/index.php/{$expected}", $uri];
  168. }
  169. return $provider;
  170. }
  171. /**
  172. * Tests URL::site for proper URL encoding when working with non-ASCII characters.
  173. *
  174. * @test
  175. * @dataProvider provider_site_url_encode_uri
  176. */
  177. public function test_site_url_encode_uri($expected, $uri)
  178. {
  179. $this->assertSame($expected, URL::site($uri, FALSE));
  180. }
  181. /**
  182. * Provides test data for test_title()
  183. * @return array
  184. */
  185. public function provider_title()
  186. {
  187. return [
  188. // Tests that..
  189. // Title is converted to lowercase
  190. ['we-shall-not-be-moved', 'WE SHALL NOT BE MOVED', '-'],
  191. // Excessive white space is removed and replaced with 1 char
  192. ['thissssss-is-it', 'THISSSSSS IS IT ', '-'],
  193. // separator is either - (dash) or _ (underscore) & others are converted to underscores
  194. ['some-title', 'some title', '-'],
  195. ['some_title', 'some title', '_'],
  196. ['some!title', 'some title', '!'],
  197. ['some:title', 'some title', ':'],
  198. // Numbers are preserved
  199. ['99-ways-to-beat-apple', '99 Ways to beat apple', '-'],
  200. // ... with lots of spaces & caps
  201. ['99_ways_to_beat_apple', '99 ways TO beat APPLE', '_'],
  202. ['99-ways-to-beat-apple', '99 ways TO beat APPLE', '-'],
  203. // Invalid characters are removed
  204. ['each-gbp-is-now-worth-32-usd', 'Each GBP(£) is now worth 32 USD($)', '-'],
  205. // ... inc. separator
  206. ['is-it-reusable-or-re-usable', 'Is it reusable or re-usable?', '-'],
  207. // Doing some crazy UTF8 tests
  208. ['espana-wins', 'España-wins', '-', TRUE],
  209. ];
  210. }
  211. /**
  212. * Tests URL::title()
  213. *
  214. * @test
  215. * @dataProvider provider_title
  216. * @param string $title Input to convert
  217. * @param string $separator Seperate to replace invalid characters with
  218. * @param string $expected Expected result
  219. */
  220. public function test_title($expected, $title, $separator, $ascii_only = FALSE)
  221. {
  222. $this->assertSame(
  223. $expected,
  224. URL::title($title, $separator, $ascii_only)
  225. );
  226. }
  227. /**
  228. * Provides test data for URL::query()
  229. * @return array
  230. */
  231. public function provider_query()
  232. {
  233. return [
  234. [[], '', NULL],
  235. [['_GET' => ['test' => 'data']], '?test=data', NULL],
  236. [[], '?test=data', ['test' => 'data']],
  237. [['_GET' => ['more' => 'data']], '?more=data&test=data', ['test' => 'data']],
  238. [['_GET' => ['sort' => 'down']], '?test=data', ['test' => 'data'], FALSE],
  239. // http://koseven.dev/issues/3362
  240. [[], '', ['key' => NULL]],
  241. [[], '?key=0', ['key' => FALSE]],
  242. [[], '?key=1', ['key' => TRUE]],
  243. [['_GET' => ['sort' => 'down']], '?sort=down&key=1', ['key' => TRUE]],
  244. [['_GET' => ['sort' => 'down']], '?sort=down&key=0', ['key' => FALSE]],
  245. // @issue 4240
  246. [['_GET' => ['foo' => ['a' => 100]]], '?foo%5Ba%5D=100&foo%5Bb%5D=bar', ['foo' => ['b' => 'bar']]],
  247. [['_GET' => ['a' => 'a']], '?a=b', ['a' => 'b']],
  248. ];
  249. }
  250. /**
  251. * Tests URL::query()
  252. *
  253. * @test
  254. * @dataProvider provider_query
  255. * @param array $enviroment Set environment
  256. * @param string $expected Expected result
  257. * @param array $params Query string
  258. * @param boolean $use_get Combine with GET parameters
  259. */
  260. public function test_query($enviroment, $expected, $params, $use_get = TRUE)
  261. {
  262. $this->setEnvironment($enviroment);
  263. $this->assertSame(
  264. $expected,
  265. URL::query($params, $use_get)
  266. );
  267. }
  268. /**
  269. * Provides test data for URL::is_trusted_host()
  270. * @return array
  271. */
  272. public function provider_is_trusted_host()
  273. {
  274. return [
  275. // data set #0
  276. [
  277. 'givenhost',
  278. [
  279. 'list-of-trusted-hosts',
  280. ],
  281. FALSE
  282. ],
  283. // data set #1
  284. [
  285. 'givenhost',
  286. [
  287. 'givenhost',
  288. 'example\.com',
  289. ],
  290. TRUE
  291. ],
  292. // data set #2
  293. [
  294. 'www.koseven.dev',
  295. [
  296. '.*\.koseven\.dev',
  297. ],
  298. TRUE
  299. ],
  300. // data set #3
  301. [
  302. 'koseven.dev',
  303. [
  304. '.*\.koseven\.dev',
  305. ],
  306. FALSE // because we are requesting a subdomain
  307. ],
  308. ];
  309. }
  310. /**
  311. * Tests URL::is_trusted_hosts()
  312. *
  313. * @test
  314. * @dataProvider provider_is_trusted_host
  315. * @param string $host the given host
  316. * @param array $trusted_hosts list of trusted hosts
  317. * @param boolean $expected TRUE if host is trusted, FALSE otherwise
  318. */
  319. public function test_is_trusted_host($host, $trusted_hosts, $expected)
  320. {
  321. $this->assertSame(
  322. $expected,
  323. URL::is_trusted_host($host, $trusted_hosts)
  324. );
  325. }
  326. /**
  327. * Tests if invalid host throws "Invalid host" exception
  328. */
  329. public function test_if_invalid_host_throws_exception()
  330. {
  331. $this->expectException(KO7_Exception::class);
  332. $this->expectExceptionMessage('Invalid host <invalid>');
  333. // set the global HTTP_HOST to <invalid>
  334. $_SERVER['HTTP_HOST'] = '<invalid>';
  335. // trigger exception
  336. URL::base('https');
  337. }
  338. /**
  339. * Tests if untrusted host throws "Untrusted host" exception
  340. */
  341. public function test_if_untrusted_host_throws_exception()
  342. {
  343. $this->expectException(KO7_Exception::class);
  344. $this->expectExceptionMessage('Untrusted host untrusted.com');
  345. // set the global HTTP_HOST to a valid but untrusted host
  346. $_SERVER['HTTP_HOST'] = 'untrusted.com';
  347. // trigger exception
  348. URL::base('https');
  349. }
  350. }