PluginProbe
Polylang / 3.7.7
Polylang v3.7.7
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 / frontend / canonical.php

canonical.php in Polylang 3.7.7, at frontend/canonical.php

275 lines 7.4 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 array
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
35 */
36 protected $curlang;
37
38 /**
39 * Constructor.
40 *
41 * @since 3.3
42 *
43 * @param object $polylang Main Polylang object.
44 */
45 public function __construct( &$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 $language = $this->curlang;
128 $redirect_url = $requested_url;
129 } else {
130 $redirect_url = $this->redirect_canonical( $requested_url, $language );
131 $redirect_url = $this->options['force_lang'] ?
132 $this->links_model->switch_language_in_link( $redirect_url, $language ) :
133 $this->links_model->remove_language_from_link( $redirect_url ); // Works only for default permalinks.
134 }
135
136
137 /**
138 * Filters the canonical url detected by Polylang.
139 *
140 * @since 1.6
141 *
142 * @param string|false $redirect_url False or the url to redirect to.
143 * @param PLL_Language $language The language detected.
144 */
145 $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
146
147 if ( ! $redirect_url || $requested_url === $redirect_url ) {
148 return $requested_url;
149 }
150
151 if ( ! $do_redirect ) {
152 return $redirect_url;
153 }
154
155 // Protect against chained redirects.
156 if ( $redirect_url === $this->check_canonical_url( $redirect_url, false ) && wp_validate_redirect( $redirect_url ) ) {
157 wp_safe_redirect( $redirect_url, 301, POLYLANG );
158 exit;
159 }
160 }
161
162 /**
163 * Returns the term_id of the requested term.
164 *
165 * @since 2.9
166 *
167 * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
168 * @return int
169 */
170 protected function get_queried_term_id( $tax_query ) {
171 $queried_terms = $tax_query->queried_terms;
172 $taxonomy = $this->get_queried_taxonomy( $tax_query );
173
174 if ( ! isset( $queried_terms[ $taxonomy ]['terms'] ) || ! is_array( $queried_terms[ $taxonomy ]['terms'] ) ) {
175 return 0;
176 }
177
178 if ( ! isset( $queried_terms[ $taxonomy ]['field'] ) ) {
179 return 0;
180 }
181
182 $field = $queried_terms[ $taxonomy ]['field'];
183 $term = reset( $queried_terms[ $taxonomy ]['terms'] );
184 $lang = isset( $queried_terms['language']['terms'] ) ? reset( $queried_terms['language']['terms'] ) : '';
185
186 // We can get a term_id when requesting a plain permalink, eg /?cat=1.
187 if ( 'term_id' === $field ) {
188 return $term;
189 }
190
191 // We get a slug when requesting a pretty permalink. Let's query all corresponding terms.
192 $args = array(
193 'lang' => '',
194 'taxonomy' => $taxonomy,
195 $field => $term,
196 'hide_empty' => false,
197 'fields' => 'ids',
198 );
199 $term_ids = get_terms( $args );
200
201 if ( ! is_array( $term_ids ) || empty( $term_ids ) ) {
202 return 0;
203 }
204
205 $term_ids = array_filter( $term_ids, 'is_numeric' );
206
207 $filtered_terms_by_lang = array_filter(
208 $term_ids,
209 function ( $term_id ) use ( $lang ) {
210 $term_lang = $this->model->term->get_language( (int) $term_id );
211
212 return ! empty( $term_lang ) && $term_lang->slug === $lang;
213 }
214 );
215
216 $tr_term = (int) reset( $filtered_terms_by_lang );
217
218 if ( ! empty( $tr_term ) ) {
219 // The queried term exists in the desired language.
220 return $tr_term;
221 }
222
223 // The queried term doesn't exist in the desired language, let's return the first one retrieved.
224 return (int) reset( $term_ids );
225 }
226
227 /**
228 * Find the taxonomy being queried.
229 *
230 * @since 2.9
231 *
232 * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
233 * @return string A taxonomy slug
234 */
235 protected function get_queried_taxonomy( $tax_query ) {
236 $queried_terms = $tax_query->queried_terms;
237 unset( $queried_terms['language'] );
238
239 return (string) key( $queried_terms );
240 }
241
242 /**
243 * Evaluates the canonical redirect url through the deidcated WP function.
244 *
245 * @since 3.3
246 *
247 * @global WP_Query $wp_query WordPress Query object.
248 *
249 * @param string $url Requested url.
250 * @param PLL_Language $language Language of the queried object.
251 * @return string
252 */
253 protected function redirect_canonical( $url, $language ) {
254 /**
255 * @var WP_Query
256 */
257 global $wp_query;
258
259 $this->curlang = $language; // Hack to filter the `page_for_posts` option in the correct language.
260
261 $backup_wp_query = $wp_query;
262
263 if ( isset( $wp_query->tax_query ) ) {
264 unset( $wp_query->tax_query->queried_terms['language'] );
265 unset( $wp_query->query['lang'] );
266 }
267
268 $redirect_url = redirect_canonical( $url, false );
269
270 $wp_query = $backup_wp_query;
271
272 return $redirect_url ?: $url;
273 }
274 }
275