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 / links-directory.php

links-directory.php in Polylang 3.8.6, at src/links-directory.php

293 lines 9.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 * Links model for use when the language code is added in the url as a directory
8 * for example mysite.com/en/something.
9 *
10 * @since 1.2
11 */
12 class PLL_Links_Directory extends PLL_Links_Permalinks {
13 /**
14 * Relative path to the home url.
15 *
16 * @var string
17 */
18 protected $home_relative;
19
20 /**
21 * Constructor.
22 *
23 * @since 1.2
24 *
25 * @param PLL_Model $model PLL_Model instance.
26 */
27 public function __construct( &$model ) {
28 parent::__construct( $model );
29
30 $this->home_relative = home_url( '/', 'relative' );
31 }
32
33 /**
34 * Adds hooks for rewrite rules.
35 *
36 * @since 1.6
37 *
38 * @return void
39 */
40 public function init() {
41 add_action( 'pll_prepare_rewrite_rules', array( $this, 'prepare_rewrite_rules' ) ); // Ensure it's hooked before `self::do_prepare_rewrite_rules()` is called.
42
43 parent::init();
44 }
45
46 /**
47 * Adds the language code in a url.
48 *
49 * @since 1.2
50 * @since 3.4 Accepts now a language slug.
51 *
52 * @param string $url The url to modify.
53 * @param PLL_Language|string|false $language Language object or slug.
54 * @return string The modified url.
55 */
56 public function add_language_to_link( $url, $language ) {
57 if ( $language instanceof PLL_Language ) {
58 $language = $language->slug;
59 }
60
61 if ( ! empty( $language ) ) {
62 $base = $this->options['rewrite'] ? '' : 'language/';
63 $slug = $this->options['default_lang'] === $language && $this->options['hide_default'] ? '' : $base . $language . '/';
64 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
65
66 if ( false === strpos( $url, $new = $root . $slug ) ) {
67 $pattern = preg_quote( $root, '#' );
68 $pattern = '#' . $pattern . '#';
69 return preg_replace( $pattern, $new, $url, 1 ); // Only once.
70 }
71 }
72 return $url;
73 }
74
75 /**
76 * Returns the url without the language code.
77 *
78 * @since 1.2
79 *
80 * @param string $url The url to modify.
81 * @return string The modified url.
82 */
83 public function remove_language_from_link( $url ) {
84 $languages = $this->model->languages
85 ->filter( $this->options['hide_default'] ? 'hide_default' : '' )
86 ->get_list( array( 'fields' => 'slug' ) );
87
88 if ( ! empty( $languages ) ) {
89 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
90
91 $pattern = preg_quote( $root, '@' );
92 $pattern = '@' . $pattern . ( $this->options['rewrite'] ? '' : 'language/' ) . '(' . implode( '|', $languages ) . ')(([?#])|(/|$))@';
93 $url = preg_replace( $pattern, $root . '$3', $url );
94 }
95 return $url;
96 }
97
98 /**
99 * Returns the language based on the language code in the url.
100 *
101 * @since 1.2
102 * @since 2.0 Add the $url argument.
103 *
104 * @param string $url Optional, defaults to the current url.
105 * @return string The language slug.
106 */
107 public function get_language_from_url( $url = '' ) {
108 if ( empty( $url ) ) {
109 $url = pll_get_requested_url();
110 }
111
112 $path = (string) wp_parse_url( $url, PHP_URL_PATH );
113 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : $this->home . '/' . $this->root;
114
115 $pattern = (string) wp_parse_url( $root . ( $this->options['rewrite'] ? '' : 'language/' ), PHP_URL_PATH );
116 $pattern = preg_quote( $pattern, '#' );
117 $pattern = '#^' . $pattern . '(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(/|$)#';
118 return preg_match( $pattern, trailingslashit( $path ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language.
119 }
120
121 /**
122 * Returns the home url in a given language.
123 *
124 * @since 1.3.1
125 * @since 3.4 Accepts now a language slug.
126 *
127 * @param PLL_Language|string $language Language object or slug.
128 * @return string
129 */
130 public function home_url( $language ) {
131 if ( $language instanceof PLL_Language ) {
132 $language = $language->slug;
133 }
134
135 $base = $this->options['rewrite'] ? '' : 'language/';
136 $slug = $this->options['default_lang'] === $language && $this->options['hide_default'] ? '' : '/' . $this->root . $base . $language;
137 return trailingslashit( $this->home . $slug );
138 }
139
140 /**
141 * Prepares the rewrite rules filters.
142 *
143 * @since 0.8.1
144 * @since 3.5 Hooked to `pll_prepare_rewrite_rules` and remove `$pre` parameter.
145 *
146 * @return void
147 */
148 public function prepare_rewrite_rules() {
149 /*
150 * Don't modify the rules if there is no languages created yet and make sure
151 * to add the filters only once and if all custom post types and taxonomies
152 * have been registered.
153 */
154 if ( ! $this->model->has_languages() || ! did_action( 'wp_loaded' ) || ! self::$can_filter_rewrite_rules ) {
155 return;
156 }
157
158 foreach ( $this->get_rewrite_rules_filters_with_callbacks() as $rule => $callback ) {
159 add_filter( $rule, $callback );
160 }
161 }
162
163 /**
164 * The rewrite rules !
165 *
166 * Always make sure that the default language is at the end in case the language information is hidden for default language.
167 * Thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct.
168 *
169 * @since 0.8.1
170 *
171 * @param string[] $rules Rewrite rules.
172 * @return string[] Modified rewrite rules.
173 */
174 public function rewrite_rules( $rules ) {
175 $filter = str_replace( '_rewrite_rules', '', (string) current_filter() );
176
177 global $wp_rewrite;
178 $newrules = array();
179
180 $languages = $this->model->get_languages_list( array( 'fields' => 'slug' ) );
181 if ( $this->options['hide_default'] ) {
182 $languages = array_diff( $languages, array( $this->options['default_lang'] ) );
183 }
184
185 if ( ! empty( $languages ) ) {
186 $slug = $wp_rewrite->root . ( $this->options['rewrite'] ? '' : 'language/' ) . '(' . implode( '|', $languages ) . ')/';
187 }
188
189 // For custom post type archives.
190 $cpts = array_intersect( $this->model->get_translated_post_types(), get_post_types( array( '_builtin' => false ) ) );
191 $cpts = $cpts ? '#post_type=(' . implode( '|', $cpts ) . ')#' : '';
192
193 foreach ( $rules as $key => $rule ) {
194 if ( ! is_string( $rule ) || ! is_string( $key ) ) {
195 // Protection against a bug in Sendinblue for WooCommerce. See: https://wordpress.org/support/topic/bug-introduced-in-rewrite-rules/
196 continue;
197 }
198
199 // Special case for translated post types and taxonomies to allow canonical redirection.
200 if ( $this->options['force_lang'] && in_array( $filter, array_merge( $this->model->get_translated_post_types(), $this->model->get_translated_taxonomies() ) ) ) {
201
202 /**
203 * Filters the rewrite rules to modify.
204 *
205 * @since 1.9.1
206 *
207 * @param bool $modify Whether to modify or not the rule, defaults to true.
208 * @param array $rule Original rewrite rule.
209 * @param string $filter Current set of rules being modified.
210 * @param string|bool $archive Custom post post type archive name or false if it is not a cpt archive.
211 */
212 if ( isset( $slug ) && apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, false ) ) {
213 $newrules[ $slug . str_replace( $wp_rewrite->root, '', ltrim( $key, '^' ) ) ] = str_replace(
214 array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
215 array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
216 $rule
217 ); // Should be enough!
218 }
219
220 $newrules[ $key ] = $rule;
221 }
222
223 // Rewrite rules filtered by language.
224 elseif ( in_array( $filter, $this->always_rewrite ) || in_array( $filter, $this->model->get_filtered_taxonomies() ) || ( $cpts && preg_match( $cpts, $rule, $matches ) && ! strpos( $rule, 'name=' ) ) || ( 'rewrite_rules_array' != $filter && $this->options['force_lang'] ) ) {
225
226 /** This filter is documented in src/links-directory.php */
227 if ( apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, empty( $matches[1] ) ? false : $matches[1] ) ) {
228 if ( isset( $slug ) ) {
229 $newrules[ $slug . str_replace( $wp_rewrite->root, '', ltrim( $key, '^' ) ) ] = str_replace(
230 array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
231 array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
232 $rule
233 ); // Should be enough!
234 }
235
236 if ( $this->options['hide_default'] ) {
237 $newrules[ $key ] = str_replace( '?', '?lang=' . $this->options['default_lang'] . '&', $rule );
238 }
239 } else {
240 $newrules[ $key ] = $rule;
241 }
242 }
243
244 // Unmodified rules.
245 else {
246 $newrules[ $key ] = $rule;
247 }
248 }
249
250 // The home rewrite rule.
251 if ( 'root' == $filter && isset( $slug ) ) {
252 $newrules[ $slug . '?$' ] = $wp_rewrite->index . '?lang=$matches[1]';
253 }
254
255 return $newrules;
256 }
257
258 /**
259 * Removes hooks to filter rewrite rules, called when switching blog @see {PLL_Base::switch_blog()}.
260 * See `self::prepare_rewrite_rules()` for added hooks.
261 *
262 * @since 3.5
263 *
264 * @return void
265 */
266 public function remove_filters() {
267 parent::remove_filters();
268
269 foreach ( $this->get_rewrite_rules_filters_with_callbacks() as $rule => $callback ) {
270 remove_filter( $rule, $callback );
271 }
272 }
273
274 /**
275 * Returns *all* rewrite rules filters with their associated callbacks.
276 *
277 * @since 3.5
278 *
279 * @return callable[] Array of hook names as key and callbacks as values.
280 */
281 protected function get_rewrite_rules_filters_with_callbacks() {
282 $filters = array(
283 'rewrite_rules_array' => array( $this, 'rewrite_rules' ), // Needed for post type archives.
284 );
285
286 foreach ( $this->get_rewrite_rules_filters() as $type ) {
287 $filters[ $type . '_rewrite_rules' ] = array( $this, 'rewrite_rules' );
288 }
289
290 return $filters;
291 }
292 }
293