PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.8
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.8
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / inc / class-rewrite.php

class-rewrite.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 27.8, at inc/class-rewrite.php

257 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Frontend
6 */
7
8 /**
9 * This code handles the category rewrites.
10 */
11 class WPSEO_Rewrite {
12
13 /**
14 * Class constructor.
15 */
16 public function __construct() {
17 add_filter( 'query_vars', [ $this, 'query_vars' ] );
18 add_filter( 'term_link', [ $this, 'no_category_base' ], 10, 3 );
19 add_filter( 'request', [ $this, 'request' ] );
20 add_filter( 'category_rewrite_rules', [ $this, 'category_rewrite_rules_wrapper' ] );
21
22 add_action( 'created_category', [ $this, 'schedule_flush' ] );
23 add_action( 'edited_category', [ $this, 'schedule_flush' ] );
24 add_action( 'delete_category', [ $this, 'schedule_flush' ] );
25 }
26
27 /**
28 * Trigger a rewrite_rule flush on shutdown.
29 *
30 * @since 1.2.8
31 *
32 * @return void
33 */
34 public function schedule_flush() {
35 if ( WPSEO_Options::get( 'stripcategorybase' ) === true ) {
36 add_action( 'shutdown', 'flush_rewrite_rules' );
37 }
38 }
39
40 /**
41 * Override the category link to remove the category base.
42 *
43 * @param string $link Term link, overridden by the function for categories.
44 * @param WP_Term $term Unused, term object.
45 * @param string $taxonomy Taxonomy slug.
46 *
47 * @return string
48 */
49 public function no_category_base( $link, $term, $taxonomy ) {
50 if ( WPSEO_Options::get( 'stripcategorybase' ) !== true ) {
51 return $link;
52 }
53
54 if ( $taxonomy !== 'category' ) {
55 return $link;
56 }
57
58 $category_base = get_option( 'category_base' );
59
60 if ( empty( $category_base ) ) {
61 $category_base = 'category';
62 }
63
64 /*
65 * Remove initial slash, if there is one (we remove the trailing slash
66 * in the regex replacement and don't want to end up short a slash).
67 */
68 if ( substr( $category_base, 0, 1 ) === '/' ) {
69 $category_base = substr( $category_base, 1 );
70 }
71
72 $category_base .= '/';
73
74 return preg_replace( '`' . preg_quote( $category_base, '`' ) . '`u', '', $link, 1 );
75 }
76
77 /**
78 * Update the query vars with the redirect var when stripcategorybase is active.
79 *
80 * @param array<string> $query_vars Main query vars to filter.
81 *
82 * @return array<string> The query vars.
83 */
84 public function query_vars( $query_vars ) {
85 if ( WPSEO_Options::get( 'stripcategorybase' ) === true ) {
86 $query_vars[] = 'wpseo_category_redirect';
87 }
88
89 return $query_vars;
90 }
91
92 /**
93 * Checks whether the redirect needs to be created.
94 *
95 * @param array<string> $query_vars Query vars to check for existence of redirect var.
96 *
97 * @return array<string> The query vars.
98 */
99 public function request( $query_vars ) {
100 if ( WPSEO_Options::get( 'stripcategorybase' ) !== true ) {
101 return $query_vars;
102 }
103
104 if ( ! isset( $query_vars['wpseo_category_redirect'] ) ) {
105 return $query_vars;
106 }
107
108 $this->redirect( $query_vars['wpseo_category_redirect'] );
109 return [];
110 }
111
112 /**
113 * Wrapper for the category_rewrite_rules() below, so we can add the $rules param in a BC way.
114 *
115 * @param array<string> $rules Rewrite rules generated for the current permastruct, keyed by their regex pattern.
116 *
117 * @return array<string> The category rewrite rules.
118 */
119 public function category_rewrite_rules_wrapper( $rules ) {
120 if ( WPSEO_Options::get( 'stripcategorybase' ) !== true ) {
121 return $rules;
122 }
123
124 return $this->category_rewrite_rules();
125 }
126
127 /**
128 * This function taken and only slightly adapted from WP No Category Base plugin by Saurabh Gupta.
129 *
130 * @return array<string> The category rewrite rules.
131 */
132 public function category_rewrite_rules() {
133 global $wp_rewrite;
134
135 $category_rewrite = [];
136
137 $taxonomy = get_taxonomy( 'category' );
138 $permalink_structure = get_option( 'permalink_structure' );
139
140 $blog_prefix = '';
141 if ( strpos( $permalink_structure, '/blog/' ) === 0 ) {
142 if ( ( is_multisite() && ! is_subdomain_install() ) || is_main_site() || is_main_network() ) {
143 $blog_prefix = 'blog/';
144 }
145 }
146
147 $categories = get_categories( [ 'hide_empty' => false ] );
148 if ( is_array( $categories ) && $categories !== [] ) {
149 foreach ( $categories as $category ) {
150 $category_nicename = $category->slug;
151 if ( $category->parent === $category->cat_ID ) {
152 // Recursive recursion.
153 $category->parent = 0;
154 }
155 elseif ( $taxonomy->rewrite['hierarchical'] !== false && $category->parent !== 0 ) {
156 $parents = get_category_parents( $category->parent, false, '/', true );
157 if ( ! is_wp_error( $parents ) ) {
158 $category_nicename = $parents . $category_nicename;
159 }
160 unset( $parents );
161 }
162
163 $category_rewrite = $this->add_category_rewrites( $category_rewrite, $category_nicename, $blog_prefix, $wp_rewrite->pagination_base );
164
165 // Adds rules for the uppercase encoded URIs.
166 $category_nicename_filtered = $this->convert_encoded_to_upper( $category_nicename );
167
168 if ( $category_nicename_filtered !== $category_nicename ) {
169 $category_rewrite = $this->add_category_rewrites( $category_rewrite, $category_nicename_filtered, $blog_prefix, $wp_rewrite->pagination_base );
170 }
171 }
172 unset( $categories, $category, $category_nicename, $category_nicename_filtered );
173 }
174
175 // Redirect support from Old Category Base.
176 $old_base = $wp_rewrite->get_category_permastruct();
177 $old_base = str_replace( '%category%', '(.+)', $old_base );
178 $old_base = trim( $old_base, '/' );
179 $category_rewrite[ $old_base . '$' ] = 'index.php?wpseo_category_redirect=$matches[1]';
180
181 return $category_rewrite;
182 }
183
184 /**
185 * Adds required category rewrites rules.
186 *
187 * @param array<string> $rewrites The current set of rules.
188 * @param string $category_name Category nicename.
189 * @param string $blog_prefix Multisite blog prefix.
190 * @param string $pagination_base WP_Query pagination base.
191 *
192 * @return array<string> The added set of rules.
193 */
194 protected function add_category_rewrites( $rewrites, $category_name, $blog_prefix, $pagination_base ) {
195 $rewrite_name = $blog_prefix . '(' . $category_name . ')';
196
197 global $wp_rewrite;
198 $feed_regex = '(' . implode( '|', $wp_rewrite->feeds ) . ')';
199
200 $rewrites[ $rewrite_name . '/(?:feed/)?' . $feed_regex . '/?$' ] = 'index.php?category_name=$matches[1]&feed=$matches[2]';
201 $rewrites[ $rewrite_name . '/' . $pagination_base . '/?([0-9]{1,})/?$' ] = 'index.php?category_name=$matches[1]&paged=$matches[2]';
202 $rewrites[ $rewrite_name . '/?$' ] = 'index.php?category_name=$matches[1]';
203
204 return $rewrites;
205 }
206
207 /**
208 * Walks through category nicename and convert encoded parts
209 * into uppercase using $this->encode_to_upper().
210 *
211 * @param string $name The encoded category URI string.
212 *
213 * @return string The convered URI string.
214 */
215 protected function convert_encoded_to_upper( $name ) {
216 // Checks if name has any encoding in it.
217 if ( strpos( $name, '%' ) === false ) {
218 return $name;
219 }
220
221 $names = explode( '/', $name );
222 $names = array_map( [ $this, 'encode_to_upper' ], $names );
223
224 return implode( '/', $names );
225 }
226
227 /**
228 * Converts the encoded URI string to uppercase.
229 *
230 * @param string $encoded The encoded string.
231 *
232 * @return string The uppercased string.
233 */
234 public function encode_to_upper( $encoded ) {
235 if ( strpos( $encoded, '%' ) === false ) {
236 return $encoded;
237 }
238
239 return strtoupper( $encoded );
240 }
241
242 /**
243 * Redirect the "old" category URL to the new one.
244 *
245 * @codeCoverageIgnore
246 *
247 * @param string $category_redirect The category page to redirect to.
248 * @return void
249 */
250 protected function redirect( $category_redirect ) {
251 $catlink = trailingslashit( get_option( 'home' ) ) . user_trailingslashit( $category_redirect, 'category' );
252
253 wp_safe_redirect( $catlink, 301, 'Yoast SEO' );
254 exit();
255 }
256 }
257