PluginProbe
Polylang / 3.8.6
Polylang v3.8.6
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / src / frontend / canonical.php

canonical.php in Polylang 3.8.6, at src/frontend/canonical.php

276 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Manages canonical redirect on frontend.
8 *
9 * @since 3.3
10 */
11 class PLL_Canonical {
12 /**
13 * Stores the plugin options.
14 *
15 * @var \WP_Syntex\Polylang\Options\Options
16 */
17 protected $options;
18
19 /**
20 * @var PLL_Model
21 */
22 protected $model;
23
24 /**
25 * Instance of a child class of PLL_Links_Model.
26 *
27 * @var PLL_Links_Model
28 */
29 protected $links_model;
30
31 /**
32 * Current language.
33 *
34 * @var PLL_Language|null
35 */
36 protected $curlang;
37
38 /**
39 * Constructor.
40 *
41 * @since 3.3
42 *
43 * @param PLL_Frontend $polylang Main Polylang object.
44 */
45 public function __construct( PLL_Frontend &$polylang ) {
46 $this->links_model = &$polylang->links_model;
47 $this->model = &$polylang->model;
48 $this->options = $polylang->options;
49 $this->curlang = &$polylang->curlang;
50 }
51
52 /**
53 * If the language code is not in agreement with the language of the content,
54 * redirects incoming links to the proper URL to avoid duplicate content.
55 *
56 * @since 0.9.6
57 *
58 * @global WP_Query $wp_query WordPress Query object.
59 * @global bool $is_IIS
60 *
61 * @param string $requested_url Optional, defaults to requested url.
62 * @param bool $do_redirect Optional, whether to perform the redirect or not.
63 * @return string|void Returns if redirect is not performed.
64 */
65 public function check_canonical_url( $requested_url = '', $do_redirect = true ) {
66 global $wp_query;
67
68 // Don't redirect in same cases as WP.
69 if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $GLOBALS['is_IIS'] && ! iis7_supports_permalinks() ) ) {
70 return;
71 }
72
73 // Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id=.
74 if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
75 return;
76 }
77
78 /*
79 * If the default language code is not hidden and the static front page url contains the page name,
80 * the customizer lands here and the code below would redirect to the list of posts.
81 */
82 if ( is_customize_preview() ) {
83 return;
84 }
85
86 if ( empty( $requested_url ) ) {
87 $requested_url = pll_get_requested_url();
88 }
89
90 if ( ( is_single() && ( ! is_attachment() || get_option( 'wp_attachment_pages_enabled' ) ) ) || ( is_page() && ! is_front_page() ) ) {
91 $post = get_post();
92 if ( $post instanceof WP_Post && $this->model->is_translated_post_type( $post->post_type ) ) {
93 $language = $this->model->post->get_language( (int) $post->ID );
94 }
95 }
96
97 if ( ! empty( $wp_query->tax_query ) ) {
98 if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $wp_query->tax_query ) ) ) {
99 $term_id = $this->get_queried_term_id( $wp_query->tax_query );
100 if ( $term_id ) {
101 $language = $this->model->term->get_language( $term_id );
102 }
103 }
104 }
105
106 if ( $wp_query->is_posts_page ) {
107 $page_id = get_query_var( 'page_id' );
108 if ( ! $page_id ) {
109 $page_id = get_queried_object_id();
110 }
111 if ( $page_id && is_numeric( $page_id ) ) {
112 $language = $this->model->post->get_language( (int) $page_id );
113 }
114 }
115
116 if ( 3 === $this->options['force_lang'] ) {
117 $requested_host = wp_parse_url( $requested_url, PHP_URL_HOST );
118 foreach ( $this->options['domains'] as $lang => $domain ) {
119 $host = wp_parse_url( $domain, PHP_URL_HOST );
120 if ( $requested_host && $host && ltrim( $requested_host, 'w.' ) === ltrim( $host, 'w.' ) ) {
121 $language = $this->model->get_language( $lang );
122 }
123 }
124 }
125
126 if ( empty( $language ) ) {
127 /** @var PLL_Language $language */
128 $language = $this->curlang;
129 $redirect_url = $requested_url;
130 } else {
131 $redirect_url = $this->redirect_canonical( $requested_url, $language );
132 $redirect_url = $this->options['force_lang'] ?
133 $this->links_model->switch_language_in_link( $redirect_url, $language ) :
134 $this->links_model->remove_language_from_link( $redirect_url ); // Works only for default permalinks.
135 }
136
137
138 /**
139 * Filters the canonical url detected by Polylang.
140 *
141 * @since 1.6
142 *
143 * @param string|false $redirect_url False or the url to redirect to.
144 * @param PLL_Language $language The language detected.
145 */
146 $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
147
148 if ( ! $redirect_url || $requested_url === $redirect_url ) {
149 return $requested_url;
150 }
151
152 if ( ! $do_redirect ) {
153 return $redirect_url;
154 }
155
156 // Protect against chained redirects.
157 if ( $redirect_url === $this->check_canonical_url( $redirect_url, false ) && wp_validate_redirect( $redirect_url ) ) {
158 wp_safe_redirect( $redirect_url, 301, POLYLANG );
159 exit;
160 }
161 }
162
163 /**
164 * Returns the term_id of the requested term.
165 *
166 * @since 2.9
167 *
168 * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
169 * @return int
170 */
171 protected function get_queried_term_id( $tax_query ) {
172 $queried_terms = $tax_query->queried_terms;
173 $taxonomy = $this->get_queried_taxonomy( $tax_query );
174
175 if ( ! isset( $queried_terms[ $taxonomy ]['terms'] ) || ! is_array( $queried_terms[ $taxonomy ]['terms'] ) ) {
176 return 0;
177 }
178
179 if ( ! isset( $queried_terms[ $taxonomy ]['field'] ) ) {
180 return 0;
181 }
182
183 $field = $queried_terms[ $taxonomy ]['field'];
184 $term = reset( $queried_terms[ $taxonomy ]['terms'] );
185 $lang = isset( $queried_terms['language']['terms'] ) ? reset( $queried_terms['language']['terms'] ) : '';
186
187 // We can get a term_id when requesting a plain permalink, eg /?cat=1.
188 if ( 'term_id' === $field ) {
189 return $term;
190 }
191
192 // We get a slug when requesting a pretty permalink. Let's query all corresponding terms.
193 $args = array(
194 'lang' => '',
195 'taxonomy' => $taxonomy,
196 $field => $term,
197 'hide_empty' => false,
198 'fields' => 'ids',
199 );
200 $term_ids = get_terms( $args );
201
202 if ( ! is_array( $term_ids ) || empty( $term_ids ) ) {
203 return 0;
204 }
205
206 $term_ids = array_filter( $term_ids, 'is_numeric' );
207
208 $filtered_terms_by_lang = array_filter(
209 $term_ids,
210 function ( $term_id ) use ( $lang ) {
211 $term_lang = $this->model->term->get_language( (int) $term_id );
212
213 return ! empty( $term_lang ) && $term_lang->slug === $lang;
214 }
215 );
216
217 $tr_term = (int) reset( $filtered_terms_by_lang );
218
219 if ( ! empty( $tr_term ) ) {
220 // The queried term exists in the desired language.
221 return $tr_term;
222 }
223
224 // The queried term doesn't exist in the desired language, let's return the first one retrieved.
225 return (int) reset( $term_ids );
226 }
227
228 /**
229 * Find the taxonomy being queried.
230 *
231 * @since 2.9
232 *
233 * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
234 * @return string A taxonomy slug
235 */
236 protected function get_queried_taxonomy( $tax_query ) {
237 $queried_terms = $tax_query->queried_terms;
238 unset( $queried_terms['language'] );
239
240 return (string) key( $queried_terms );
241 }
242
243 /**
244 * Evaluates the canonical redirect url through the dedicated WP function.
245 *
246 * @since 3.3
247 *
248 * @global WP_Query $wp_query WordPress Query object.
249 *
250 * @param string $url Requested url.
251 * @param PLL_Language $language Language of the queried object.
252 * @return string
253 */
254 protected function redirect_canonical( $url, $language ) {
255 /**
256 * @var WP_Query
257 */
258 global $wp_query;
259
260 $this->curlang = $language; // Hack to filter the `page_for_posts` option in the correct language.
261
262 $backup_wp_query = $wp_query;
263
264 if ( isset( $wp_query->tax_query ) ) {
265 unset( $wp_query->tax_query->queried_terms['language'] );
266 unset( $wp_query->query['lang'] );
267 }
268
269 $redirect_url = redirect_canonical( $url, false );
270
271 $wp_query = $backup_wp_query;
272
273 return $redirect_url ?: $url;
274 }
275 }
276