PluginProbe ʕ •ᴥ•ʔ
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 25.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v25.5
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 19.1 19.10 19.11 19.12 19.13 19.14 19.2 19.3 19.4 19.5 19.5.1 19.6 19.6.1 19.7 19.7.1 19.7.2 19.8 19.9 20.0 20.1 20.10 20.11 20.12 20.13 20.2 20.2.1 20.3 20.4 20.5 20.6 20.7 20.8 20.9 21.0 21.1 21.2 21.3 21.4 21.5 21.6 21.7 21.8 21.8.1 21.9 21.9.1 22.0 22.1 22.2 22.3 22.4 22.5 22.6 22.7 22.8 22.9 23.0 23.1 23.2 23.3 23.4 23.5 23.6 23.7 23.8 23.9 24.0 24.1 24.2 24.3 24.4 24.5 24.6 24.7 24.8 24.8.1 24.9 25.0 25.1 25.2 25.3 25.3.1 25.4 25.5 25.6 25.7 25.8 25.9 26.0 26.1 26.1.1 26.2 26.3 26.4 26.5 26.6 26.7 26.8 26.9 27.0 27.1 27.1.1 27.2 27.3 27.4
wordpress-seo / inc / class-rewrite.php
wordpress-seo / inc Last commit date
exceptions 5 years ago options 1 year ago sitemaps 1 year ago class-addon-manager.php 11 months ago class-my-yoast-api-request.php 1 year ago class-post-type.php 1 year ago class-rewrite.php 1 year ago class-upgrade-history.php 1 year ago class-upgrade.php 1 year ago class-wpseo-admin-bar-menu.php 1 year ago class-wpseo-content-images.php 1 year ago class-wpseo-custom-fields.php 1 year ago class-wpseo-custom-taxonomies.php 1 year ago class-wpseo-image-utils.php 1 year ago class-wpseo-installation.php 2 years ago class-wpseo-meta.php 1 year ago class-wpseo-primary-term.php 2 years ago class-wpseo-rank.php 1 year ago class-wpseo-replace-vars.php 1 year ago class-wpseo-replacement-variable.php 5 years ago class-wpseo-shortlinker.php 2 years ago class-wpseo-statistics.php 5 years ago class-wpseo-utils.php 11 months ago class-yoast-dynamic-rewrites.php 2 years ago date-helper.php 5 years ago index.php 10 years ago interface-wpseo-wordpress-ajax-integration.php 7 years ago interface-wpseo-wordpress-integration.php 7 years ago language-utils.php 2 years ago wpseo-functions-deprecated.php 2 years ago wpseo-functions.php 2 years ago wpseo-non-ajax-functions.php 5 years ago
class-rewrite.php
257 lines
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