canonical.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <?php
  2. /**
  3. * Tests Canonical redirections.
  4. *
  5. * In the process of doing so, it also tests WP, WP_Rewrite and WP_Query, A fail here may show a bug in any one of these areas.
  6. *
  7. * @group canonical
  8. * @group rewrite
  9. * @group query
  10. */
  11. class Tests_Canonical extends WP_Canonical_UnitTestCase {
  12. public function setUp() {
  13. parent::setUp();
  14. wp_set_current_user( self::$author_id );
  15. }
  16. /**
  17. * @dataProvider data_canonical
  18. */
  19. function test_canonical( $test_url, $expected, $ticket = 0, $expected_doing_it_wrong = array() ) {
  20. if ( false !== strpos( $test_url, '%d' ) ) {
  21. if ( false !== strpos( $test_url, '/?author=%d' ) ) {
  22. $test_url = sprintf( $test_url, self::$author_id );
  23. }
  24. if ( false !== strpos( $test_url, '?cat=%d' ) ) {
  25. $test_url = sprintf( $test_url, self::$terms[ $expected['url'] ] );
  26. }
  27. }
  28. $this->assertCanonical( $test_url, $expected, $ticket, $expected_doing_it_wrong );
  29. }
  30. function data_canonical() {
  31. /*
  32. * Data format:
  33. * [0]: Test URL.
  34. * [1]: Expected results: Any of the following can be used.
  35. * array( 'url': expected redirection location, 'qv': expected query vars to be set via the rewrite AND $_GET );
  36. * array( expected query vars to be set, same as 'qv' above )
  37. * (string) expected redirect location
  38. * [2]: (optional) The ticket the test refers to, Can be skipped if unknown.
  39. * [3]: (optional) Array of class/function names expected to throw `_doing_it_wrong()` notices.
  40. */
  41. // Please Note: A few test cases are commented out below, look at the test case following it.
  42. // In most cases it's simply showing 2 options for the "proper" redirect.
  43. return array(
  44. // Categories.
  45. array( '?cat=%d', array( 'url' => '/category/parent/' ), 15256 ),
  46. array( '?cat=%d', array( 'url' => '/category/parent/child-1/' ), 15256 ),
  47. array( '?cat=%d', array( 'url' => '/category/parent/child-1/child-2/' ) ), // No children.
  48. array(
  49. '/category/uncategorized/',
  50. array(
  51. 'url' => '/category/uncategorized/',
  52. 'qv' => array( 'category_name' => 'uncategorized' ),
  53. ),
  54. ),
  55. array(
  56. '/category/uncategorized/page/2/',
  57. array(
  58. 'url' => '/category/uncategorized/page/2/',
  59. 'qv' => array(
  60. 'category_name' => 'uncategorized',
  61. 'paged' => 2,
  62. ),
  63. ),
  64. ),
  65. array(
  66. '/category/uncategorized/?paged=2',
  67. array(
  68. 'url' => '/category/uncategorized/page/2/',
  69. 'qv' => array(
  70. 'category_name' => 'uncategorized',
  71. 'paged' => 2,
  72. ),
  73. ),
  74. ),
  75. array(
  76. '/category/uncategorized/?paged=2&category_name=uncategorized',
  77. array(
  78. 'url' => '/category/uncategorized/page/2/',
  79. 'qv' => array(
  80. 'category_name' => 'uncategorized',
  81. 'paged' => 2,
  82. ),
  83. ),
  84. 17174,
  85. ),
  86. // Categories & intersections with other vars.
  87. array(
  88. '/category/uncategorized/?tag=post-formats',
  89. array(
  90. 'url' => '/category/uncategorized/?tag=post-formats',
  91. 'qv' => array(
  92. 'category_name' => 'uncategorized',
  93. 'tag' => 'post-formats',
  94. ),
  95. ),
  96. ),
  97. array(
  98. '/?category_name=cat-a,cat-b',
  99. array(
  100. 'url' => '/?category_name=cat-a,cat-b',
  101. 'qv' => array( 'category_name' => 'cat-a,cat-b' ),
  102. ),
  103. ),
  104. // Taxonomies with extra query vars.
  105. array( '/category/cat-a/page/1/?test=one%20two', '/category/cat-a/?test=one%20two', 18086 ), // Extra query vars should stay encoded.
  106. // Categories with dates.
  107. array(
  108. '/2008/04/?cat=1',
  109. array(
  110. 'url' => '/2008/04/?cat=1',
  111. 'qv' => array(
  112. 'cat' => '1',
  113. 'year' => '2008',
  114. 'monthnum' => '04',
  115. ),
  116. ),
  117. 17661,
  118. ),
  119. /*
  120. array(
  121. '/2008/?category_name=cat-a',
  122. array(
  123. 'url' => '/2008/?category_name=cat-a',
  124. 'qv' => array(
  125. 'category_name' => 'cat-a',
  126. 'year' => '2008'
  127. )
  128. )
  129. ),
  130. */
  131. // Pages.
  132. array( '/child-page-1/', '/parent-page/child-page-1/' ),
  133. array( '/?page_id=144', '/parent-page/child-page-1/' ),
  134. array( '/abo', '/about/' ),
  135. array( '/parent/child1/grandchild/', '/parent/child1/grandchild/' ),
  136. array( '/parent/child2/grandchild/', '/parent/child2/grandchild/' ),
  137. // Posts.
  138. array( '?p=587', '/2008/06/02/post-format-test-audio/' ),
  139. array( '/?name=images-test', '/2008/09/03/images-test/' ),
  140. // Incomplete slug should resolve and remove the ?name= parameter.
  141. array( '/?name=images-te', '/2008/09/03/images-test/', 20374 ),
  142. // Page slug should resolve to post slug and remove the ?pagename= parameter.
  143. array( '/?pagename=images-test', '/2008/09/03/images-test/', 20374 ),
  144. array( '/2008/06/02/post-format-test-au/', '/2008/06/02/post-format-test-audio/' ),
  145. array( '/2008/06/post-format-test-au/', '/2008/06/02/post-format-test-audio/' ),
  146. array( '/2008/post-format-test-au/', '/2008/06/02/post-format-test-audio/' ),
  147. array( '/2010/post-format-test-au/', '/2008/06/02/post-format-test-audio/' ), // A year the post is not in.
  148. array( '/post-format-test-au/', '/2008/06/02/post-format-test-audio/' ),
  149. // Pagination.
  150. array(
  151. '/2008/09/03/multipage-post-test/3/',
  152. array(
  153. 'url' => '/2008/09/03/multipage-post-test/3/',
  154. 'qv' => array(
  155. 'name' => 'multipage-post-test',
  156. 'year' => '2008',
  157. 'monthnum' => '09',
  158. 'day' => '03',
  159. 'page' => '3',
  160. ),
  161. ),
  162. ),
  163. array( '/2008/09/03/multipage-post-test/?page=3', '/2008/09/03/multipage-post-test/3/' ),
  164. array( '/2008/09/03/multipage-post-te?page=3', '/2008/09/03/multipage-post-test/3/' ),
  165. array( '/2008/09/03/non-paged-post-test/3/', '/2008/09/03/non-paged-post-test/' ),
  166. array( '/2008/09/03/non-paged-post-test/?page=3', '/2008/09/03/non-paged-post-test/' ),
  167. // Comments.
  168. array( '/2008/03/03/comment-test/?cpage=2', '/2008/03/03/comment-test/comment-page-2/' ),
  169. // Attachments.
  170. array( '/?attachment_id=611', '/2008/06/10/post-format-test-gallery/canola2/' ),
  171. array( '/2008/06/10/post-format-test-gallery/?attachment_id=611', '/2008/06/10/post-format-test-gallery/canola2/' ),
  172. // Dates.
  173. array( '/?m=2008', '/2008/' ),
  174. array( '/?m=200809', '/2008/09/' ),
  175. array( '/?m=20080905', '/2008/09/05/' ),
  176. array( '/2008/?day=05', '/2008/?day=05' ), // No redirect.
  177. array( '/2008/09/?day=05', '/2008/09/05/' ),
  178. array( '/2008/?monthnum=9', '/2008/09/' ),
  179. array( '/?year=2008', '/2008/' ),
  180. array( '/2012/13/', '/2012/' ),
  181. array( '/2012/11/51/', '/2012/11/', 0, array( 'WP_Date_Query' ) ),
  182. // Authors.
  183. array( '/?author=%d', '/author/canonical-author/' ),
  184. // array( '/?author=%d&year=2008', '/2008/?author=3'),
  185. // array( '/author/canonical-author/?year=2008', '/2008/?author=3'), // Either or, see previous testcase.
  186. // Feeds.
  187. array( '/?feed=atom', '/feed/atom/' ),
  188. array( '/?feed=rss2', '/feed/' ),
  189. array( '/?feed=comments-rss2', '/comments/feed/' ),
  190. array( '/?feed=comments-atom', '/comments/feed/atom/' ),
  191. // Feeds (per-post).
  192. array( '/2008/03/03/comment-test/?feed=comments-atom', '/2008/03/03/comment-test/feed/atom/' ),
  193. array( '/?p=149&feed=comments-atom', '/2008/03/03/comment-test/feed/atom/' ),
  194. // Index.
  195. array( '/?paged=1', '/' ),
  196. array( '/page/1/', '/' ),
  197. array( '/page1/', '/' ),
  198. array( '/?paged=2', '/page/2/' ),
  199. array( '/page2/', '/page/2/' ),
  200. // Misc.
  201. array( '/2008%20', '/2008' ),
  202. array( '//2008////', '/2008/' ),
  203. // @todo Endpoints (feeds, trackbacks, etc). More fuzzed mixed query variables, comment paging, Home page (static).
  204. );
  205. }
  206. /**
  207. * @ticket 16557
  208. */
  209. public function test_do_redirect_guess_404_permalink() {
  210. // Test disable do_redirect_guess_404_permalink().
  211. add_filter( 'do_redirect_guess_404_permalink', '__return_false' );
  212. $this->go_to( '/child-page-1' );
  213. $this->assertFalse( redirect_guess_404_permalink() );
  214. }
  215. /**
  216. * @ticket 16557
  217. */
  218. public function test_pre_redirect_guess_404_permalink() {
  219. // Test short-circuit filter.
  220. add_filter(
  221. 'pre_redirect_guess_404_permalink',
  222. function() {
  223. return 'wp';
  224. }
  225. );
  226. $this->go_to( '/child-page-1' );
  227. $this->assertSame( 'wp', redirect_guess_404_permalink() );
  228. }
  229. /**
  230. * @ticket 16557
  231. */
  232. public function test_strict_redirect_guess_404_permalink() {
  233. $post = self::factory()->post->create(
  234. array(
  235. 'post_title' => 'strict-redirect-guess-404-permalink',
  236. )
  237. );
  238. $this->go_to( 'strict-redirect' );
  239. // Test default 'non-strict' redirect guess.
  240. $this->assertSame( get_permalink( $post ), redirect_guess_404_permalink() );
  241. // Test 'strict' redirect guess.
  242. add_filter( 'strict_redirect_guess_404_permalink', '__return_true' );
  243. $this->assertFalse( redirect_guess_404_permalink() );
  244. }
  245. /**
  246. * Ensure multiple post types do not throw a notice.
  247. *
  248. * @ticket 43056
  249. */
  250. public function test_redirect_guess_404_permalink_post_types() {
  251. /*
  252. * Sample-page is intentionally missspelt as sample-pag to ensure
  253. * the 404 post permalink guessing runs.
  254. *
  255. * Please do not correct the apparent typo.
  256. */
  257. // String format post type.
  258. $this->assertCanonical( '/?name=sample-pag&post_type=page', '/sample-page/' );
  259. // Array formatted post type or types.
  260. $this->assertCanonical( '/?name=sample-pag&post_type[]=page', '/sample-page/' );
  261. $this->assertCanonical( '/?name=sample-pag&post_type[]=page&post_type[]=post', '/sample-page/' );
  262. }
  263. /**
  264. * @ticket 43745
  265. */
  266. public function test_utf8_query_keys_canonical() {
  267. $p = self::factory()->post->create(
  268. array(
  269. 'post_type' => 'page',
  270. )
  271. );
  272. update_option( 'show_on_front', 'page' );
  273. update_option( 'page_on_front', $p );
  274. $this->go_to( get_permalink( $p ) );
  275. $url = redirect_canonical( add_query_arg( '%D0%BA%D0%BE%D0%BA%D0%BE%D0%BA%D0%BE', 1, site_url( '/' ) ), false );
  276. $this->assertNull( $url );
  277. delete_option( 'page_on_front' );
  278. }
  279. }