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