182 |
183 |
183 /** |
184 /** |
184 * Rewrite rules to match against the request to find the redirect or query. |
185 * Rewrite rules to match against the request to find the redirect or query. |
185 * |
186 * |
186 * @since 1.5.0 |
187 * @since 1.5.0 |
187 * @var array |
188 * @var string[] |
188 */ |
189 */ |
189 public $rules; |
190 public $rules; |
190 |
191 |
191 /** |
192 /** |
192 * Additional rules added external to the rewrite class. |
193 * Additional rules added external to the rewrite class. |
193 * |
194 * |
194 * Those not generated by the class, see add_rewrite_rule(). |
195 * Those not generated by the class, see add_rewrite_rule(). |
195 * |
196 * |
196 * @since 2.1.0 |
197 * @since 2.1.0 |
197 * @var array |
198 * @var string[] |
198 */ |
199 */ |
199 public $extra_rules = array(); |
200 public $extra_rules = array(); |
200 |
201 |
201 /** |
202 /** |
202 * Additional rules that belong at the beginning to match first. |
203 * Additional rules that belong at the beginning to match first. |
203 * |
204 * |
204 * Those not generated by the class, see add_rewrite_rule(). |
205 * Those not generated by the class, see add_rewrite_rule(). |
205 * |
206 * |
206 * @since 2.3.0 |
207 * @since 2.3.0 |
207 * @var array |
208 * @var string[] |
208 */ |
209 */ |
209 public $extra_rules_top = array(); |
210 public $extra_rules_top = array(); |
210 |
211 |
211 /** |
212 /** |
212 * Rules that don't redirect to WordPress' index.php. |
213 * Rules that don't redirect to WordPress' index.php. |
213 * |
214 * |
214 * These rules are written to the mod_rewrite portion of the .htaccess, |
215 * These rules are written to the mod_rewrite portion of the .htaccess, |
215 * and are added by add_external_rule(). |
216 * and are added by add_external_rule(). |
216 * |
217 * |
217 * @since 2.1.0 |
218 * @since 2.1.0 |
218 * @var array |
219 * @var string[] |
219 */ |
220 */ |
220 public $non_wp_rules = array(); |
221 public $non_wp_rules = array(); |
221 |
222 |
222 /** |
223 /** |
223 * Extra permalink structures, e.g. categories, added by add_permastruct(). |
224 * Extra permalink structures, e.g. categories, added by add_permastruct(). |
224 * |
225 * |
225 * @since 2.1.0 |
226 * @since 2.1.0 |
226 * @var array |
227 * @var array[] |
227 */ |
228 */ |
228 public $extra_permastructs = array(); |
229 public $extra_permastructs = array(); |
229 |
230 |
230 /** |
231 /** |
231 * Endpoints (like /trackback/) added by add_rewrite_endpoint(). |
232 * Endpoints (like /trackback/) added by add_rewrite_endpoint(). |
232 * |
233 * |
233 * @since 2.1.0 |
234 * @since 2.1.0 |
234 * @var array |
235 * @var array[] |
235 */ |
236 */ |
236 public $endpoints; |
237 public $endpoints; |
237 |
238 |
238 /** |
239 /** |
239 * Whether to write every mod_rewrite rule for WordPress into the .htaccess file. |
240 * Whether to write every mod_rewrite rule for WordPress into the .htaccess file. |
613 public function get_category_permastruct() { |
614 public function get_category_permastruct() { |
614 return $this->get_extra_permastruct( 'category' ); |
615 return $this->get_extra_permastruct( 'category' ); |
615 } |
616 } |
616 |
617 |
617 /** |
618 /** |
618 * Retrieve the permalink structure for tags. |
619 * Retrieves the permalink structure for tags. |
619 * |
620 * |
620 * If the tag_base property has no value, then the tag structure will have |
621 * If the tag_base property has no value, then the tag structure will have |
621 * the front property value, followed by 'tag', and finally '%tag%'. If it |
622 * the front property value, followed by 'tag', and finally '%tag%'. If it |
622 * does, then the root property will be used, along with the tag_base |
623 * does, then the root property will be used, along with the tag_base |
623 * property value. |
624 * property value. |
858 * - `EP_TAGS` |
859 * - `EP_TAGS` |
859 * - `EP_YEAR` |
860 * - `EP_YEAR` |
860 * Default `EP_NONE`. |
861 * Default `EP_NONE`. |
861 * @param bool $paged Optional. Whether archive pagination rules should be added for the structure. |
862 * @param bool $paged Optional. Whether archive pagination rules should be added for the structure. |
862 * Default true. |
863 * Default true. |
863 * @param bool $feed Optional Whether feed rewrite rules should be added for the structure. |
864 * @param bool $feed Optional. Whether feed rewrite rules should be added for the structure. |
864 * Default true. |
865 * Default true. |
865 * @param bool $forcomments Optional. Whether the feed rules should be a query for a comments feed. |
866 * @param bool $forcomments Optional. Whether the feed rules should be a query for a comments feed. |
866 * Default false. |
867 * Default false. |
867 * @param bool $walk_dirs Optional. Whether the 'directories' making up the structure should be walked |
868 * @param bool $walk_dirs Optional. Whether the 'directories' making up the structure should be walked |
868 * over and rewrite rules built for each in-turn. Default true. |
869 * over and rewrite rules built for each in-turn. Default true. |
1057 * Check to see if this dir is permalink-level: i.e. the structure specifies an |
1058 * Check to see if this dir is permalink-level: i.e. the structure specifies an |
1058 * individual post. Do this by checking it contains at least one of 1) post name, |
1059 * individual post. Do this by checking it contains at least one of 1) post name, |
1059 * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and |
1060 * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and |
1060 * minute all present). Set these flags now as we need them for the endpoints. |
1061 * minute all present). Set these flags now as we need them for the endpoints. |
1061 */ |
1062 */ |
1062 if ( strpos( $struct, '%postname%' ) !== false |
1063 if ( str_contains( $struct, '%postname%' ) |
1063 || strpos( $struct, '%post_id%' ) !== false |
1064 || str_contains( $struct, '%post_id%' ) |
1064 || strpos( $struct, '%pagename%' ) !== false |
1065 || str_contains( $struct, '%pagename%' ) |
1065 || ( strpos( $struct, '%year%' ) !== false && strpos( $struct, '%monthnum%' ) !== false && strpos( $struct, '%day%' ) !== false && strpos( $struct, '%hour%' ) !== false && strpos( $struct, '%minute%' ) !== false && strpos( $struct, '%second%' ) !== false ) |
1066 || ( str_contains( $struct, '%year%' ) |
1066 ) { |
1067 && str_contains( $struct, '%monthnum%' ) |
|
1068 && str_contains( $struct, '%day%' ) |
|
1069 && str_contains( $struct, '%hour%' ) |
|
1070 && str_contains( $struct, '%minute%' ) |
|
1071 && str_contains( $struct, '%second%' ) ) |
|
1072 ) { |
1067 $post = true; |
1073 $post = true; |
1068 if ( strpos( $struct, '%pagename%' ) !== false ) { |
1074 if ( str_contains( $struct, '%pagename%' ) ) { |
1069 $page = true; |
1075 $page = true; |
1070 } |
1076 } |
1071 } |
1077 } |
1072 |
1078 |
1073 if ( ! $post ) { |
1079 if ( ! $post ) { |
1074 // For custom post types, we need to add on endpoints as well. |
1080 // For custom post types, we need to add on endpoints as well. |
1075 foreach ( get_post_types( array( '_builtin' => false ) ) as $ptype ) { |
1081 foreach ( get_post_types( array( '_builtin' => false ) ) as $ptype ) { |
1076 if ( strpos( $struct, "%$ptype%" ) !== false ) { |
1082 if ( str_contains( $struct, "%$ptype%" ) ) { |
1077 $post = true; |
1083 $post = true; |
1078 |
1084 |
1079 // This is for page style attachment URLs. |
1085 // This is for page style attachment URLs. |
1080 $page = is_post_type_hierarchical( $ptype ); |
1086 $page = is_post_type_hierarchical( $ptype ); |
1081 break; |
1087 break; |
1242 * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters. |
1248 * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters. |
1243 * |
1249 * |
1244 * @param string $permalink_structure The permalink structure to generate rules. |
1250 * @param string $permalink_structure The permalink structure to generate rules. |
1245 * @param bool $walk_dirs Optional. Whether to create list of directories to walk over. |
1251 * @param bool $walk_dirs Optional. Whether to create list of directories to walk over. |
1246 * Default false. |
1252 * Default false. |
1247 * @return array |
1253 * @return array An array of rewrite rules keyed by their regex pattern. |
1248 */ |
1254 */ |
1249 public function generate_rewrite_rule( $permalink_structure, $walk_dirs = false ) { |
1255 public function generate_rewrite_rule( $permalink_structure, $walk_dirs = false ) { |
1250 return $this->generate_rewrite_rules( $permalink_structure, EP_NONE, false, false, false, $walk_dirs ); |
1256 return $this->generate_rewrite_rules( $permalink_structure, EP_NONE, false, false, false, $walk_dirs ); |
1251 } |
1257 } |
1252 |
1258 |
1396 $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite ); |
1402 $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite ); |
1397 |
1403 |
1398 // Extra permastructs. |
1404 // Extra permastructs. |
1399 foreach ( $this->extra_permastructs as $permastructname => $struct ) { |
1405 foreach ( $this->extra_permastructs as $permastructname => $struct ) { |
1400 if ( is_array( $struct ) ) { |
1406 if ( is_array( $struct ) ) { |
1401 if ( count( $struct ) == 2 ) { |
1407 if ( count( $struct ) === 2 ) { |
1402 $rules = $this->generate_rewrite_rules( $struct[0], $struct[1] ); |
1408 $rules = $this->generate_rewrite_rules( $struct[0], $struct[1] ); |
1403 } else { |
1409 } else { |
1404 $rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] ); |
1410 $rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] ); |
1405 } |
1411 } |
1406 } else { |
1412 } else { |
1482 * @return string[] Array of rewrite rules keyed by their regex pattern. |
1488 * @return string[] Array of rewrite rules keyed by their regex pattern. |
1483 */ |
1489 */ |
1484 public function wp_rewrite_rules() { |
1490 public function wp_rewrite_rules() { |
1485 $this->rules = get_option( 'rewrite_rules' ); |
1491 $this->rules = get_option( 'rewrite_rules' ); |
1486 if ( empty( $this->rules ) ) { |
1492 if ( empty( $this->rules ) ) { |
1487 $this->matches = 'matches'; |
1493 $this->refresh_rewrite_rules(); |
1488 $this->rewrite_rules(); |
1494 } |
1489 if ( ! did_action( 'wp_loaded' ) ) { |
1495 |
1490 add_action( 'wp_loaded', array( $this, 'flush_rules' ) ); |
1496 return $this->rules; |
1491 return $this->rules; |
1497 } |
1492 } |
1498 |
|
1499 /** |
|
1500 * Refreshes the rewrite rules, saving the fresh value to the database. |
|
1501 * If the `wp_loaded` action has not occurred yet, will postpone saving to the database. |
|
1502 * |
|
1503 * @since 6.4.0 |
|
1504 */ |
|
1505 private function refresh_rewrite_rules() { |
|
1506 $this->rules = ''; |
|
1507 $this->matches = 'matches'; |
|
1508 |
|
1509 $this->rewrite_rules(); |
|
1510 |
|
1511 if ( ! did_action( 'wp_loaded' ) ) { |
|
1512 /* |
|
1513 * Is not safe to save the results right now, as the rules may be partial. |
|
1514 * Need to give all rules the chance to register. |
|
1515 */ |
|
1516 add_action( 'wp_loaded', array( $this, 'flush_rules' ) ); |
|
1517 } else { |
1493 update_option( 'rewrite_rules', $this->rules ); |
1518 update_option( 'rewrite_rules', $this->rules ); |
1494 } |
1519 } |
1495 |
|
1496 return $this->rules; |
|
1497 } |
1520 } |
1498 |
1521 |
1499 /** |
1522 /** |
1500 * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess. |
1523 * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess. |
1501 * |
1524 * |
1552 |
1575 |
1553 foreach ( (array) $rewrite as $match => $query ) { |
1576 foreach ( (array) $rewrite as $match => $query ) { |
1554 // Apache 1.3 does not support the reluctant (non-greedy) modifier. |
1577 // Apache 1.3 does not support the reluctant (non-greedy) modifier. |
1555 $match = str_replace( '.+?', '.+', $match ); |
1578 $match = str_replace( '.+?', '.+', $match ); |
1556 |
1579 |
1557 if ( strpos( $query, $this->index ) !== false ) { |
1580 if ( str_contains( $query, $this->index ) ) { |
1558 $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n"; |
1581 $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n"; |
1559 } else { |
1582 } else { |
1560 $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n"; |
1583 $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n"; |
1561 } |
1584 } |
1562 } |
1585 } |
1657 public function add_rule( $regex, $query, $after = 'bottom' ) { |
1680 public function add_rule( $regex, $query, $after = 'bottom' ) { |
1658 if ( is_array( $query ) ) { |
1681 if ( is_array( $query ) ) { |
1659 $external = false; |
1682 $external = false; |
1660 $query = add_query_arg( $query, 'index.php' ); |
1683 $query = add_query_arg( $query, 'index.php' ); |
1661 } else { |
1684 } else { |
1662 $index = false === strpos( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' ); |
1685 $index = ! str_contains( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' ); |
1663 $front = substr( $query, 0, $index ); |
1686 $front = substr( $query, 0, $index ); |
1664 |
1687 |
1665 $external = $front != $this->index; |
1688 $external = $front !== $this->index; |
1666 } |
1689 } |
1667 |
1690 |
1668 // "external" = it doesn't correspond to index.php. |
1691 // "external" = it doesn't correspond to index.php. |
1669 if ( $external ) { |
1692 if ( $external ) { |
1670 $this->add_external_rule( $regex, $query ); |
1693 $this->add_external_rule( $regex, $query ); |
1791 public function add_permastruct( $name, $struct, $args = array() ) { |
1814 public function add_permastruct( $name, $struct, $args = array() ) { |
1792 // Back-compat for the old parameters: $with_front and $ep_mask. |
1815 // Back-compat for the old parameters: $with_front and $ep_mask. |
1793 if ( ! is_array( $args ) ) { |
1816 if ( ! is_array( $args ) ) { |
1794 $args = array( 'with_front' => $args ); |
1817 $args = array( 'with_front' => $args ); |
1795 } |
1818 } |
1796 if ( func_num_args() == 4 ) { |
1819 |
|
1820 if ( func_num_args() === 4 ) { |
1797 $args['ep_mask'] = func_get_arg( 3 ); |
1821 $args['ep_mask'] = func_get_arg( 3 ); |
1798 } |
1822 } |
1799 |
1823 |
1800 $defaults = array( |
1824 $defaults = array( |
1801 'with_front' => true, |
1825 'with_front' => true, |
1804 'feed' => true, |
1828 'feed' => true, |
1805 'forcomments' => false, |
1829 'forcomments' => false, |
1806 'walk_dirs' => true, |
1830 'walk_dirs' => true, |
1807 'endpoints' => true, |
1831 'endpoints' => true, |
1808 ); |
1832 ); |
1809 $args = array_intersect_key( $args, $defaults ); |
1833 |
1810 $args = wp_parse_args( $args, $defaults ); |
1834 $args = array_intersect_key( $args, $defaults ); |
|
1835 $args = wp_parse_args( $args, $defaults ); |
1811 |
1836 |
1812 if ( $args['with_front'] ) { |
1837 if ( $args['with_front'] ) { |
1813 $struct = $this->front . $struct; |
1838 $struct = $this->front . $struct; |
1814 } else { |
1839 } else { |
1815 $struct = $this->root . $struct; |
1840 $struct = $this->root . $struct; |
1816 } |
1841 } |
|
1842 |
1817 $args['struct'] = $struct; |
1843 $args['struct'] = $struct; |
1818 |
1844 |
1819 $this->extra_permastructs[ $name ] = $args; |
1845 $this->extra_permastructs[ $name ] = $args; |
1820 } |
1846 } |
1821 |
1847 |
1853 if ( isset( $do_hard_later ) ) { |
1879 if ( isset( $do_hard_later ) ) { |
1854 $hard = $do_hard_later; |
1880 $hard = $do_hard_later; |
1855 unset( $do_hard_later ); |
1881 unset( $do_hard_later ); |
1856 } |
1882 } |
1857 |
1883 |
1858 update_option( 'rewrite_rules', '' ); |
1884 $this->refresh_rewrite_rules(); |
1859 $this->wp_rewrite_rules(); |
|
1860 |
1885 |
1861 /** |
1886 /** |
1862 * Filters whether a "hard" rewrite rule flush should be performed when requested. |
1887 * Filters whether a "hard" rewrite rule flush should be performed when requested. |
1863 * |
1888 * |
1864 * A "hard" flush updates .htaccess (Apache) or web.config (IIS). |
1889 * A "hard" flush updates .htaccess (Apache) or web.config (IIS). |
1904 unset( $this->page_structure ); |
1929 unset( $this->page_structure ); |
1905 unset( $this->search_structure ); |
1930 unset( $this->search_structure ); |
1906 unset( $this->feed_structure ); |
1931 unset( $this->feed_structure ); |
1907 unset( $this->comment_feed_structure ); |
1932 unset( $this->comment_feed_structure ); |
1908 |
1933 |
1909 $this->use_trailing_slashes = ( '/' === substr( $this->permalink_structure, -1, 1 ) ); |
1934 $this->use_trailing_slashes = str_ends_with( $this->permalink_structure, '/' ); |
1910 |
1935 |
1911 // Enable generic rules for pages if permalink structure doesn't begin with a wildcard. |
1936 // Enable generic rules for pages if permalink structure doesn't begin with a wildcard. |
1912 if ( preg_match( '/^[^%]*%(?:postname|category|tag|author)%/', $this->permalink_structure ) ) { |
1937 if ( preg_match( '/^[^%]*%(?:postname|category|tag|author)%/', $this->permalink_structure ) ) { |
1913 $this->use_verbose_page_rules = true; |
1938 $this->use_verbose_page_rules = true; |
1914 } else { |
1939 } else { |
1929 * @since 1.5.0 |
1954 * @since 1.5.0 |
1930 * |
1955 * |
1931 * @param string $permalink_structure Permalink structure. |
1956 * @param string $permalink_structure Permalink structure. |
1932 */ |
1957 */ |
1933 public function set_permalink_structure( $permalink_structure ) { |
1958 public function set_permalink_structure( $permalink_structure ) { |
1934 if ( $permalink_structure != $this->permalink_structure ) { |
1959 if ( $this->permalink_structure !== $permalink_structure ) { |
1935 $old_permalink_structure = $this->permalink_structure; |
1960 $old_permalink_structure = $this->permalink_structure; |
1936 update_option( 'permalink_structure', $permalink_structure ); |
1961 update_option( 'permalink_structure', $permalink_structure ); |
1937 |
1962 |
1938 $this->init(); |
1963 $this->init(); |
1939 |
1964 |