PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.1
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 18.1, at inc/class-rewrite.php

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