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 / language-factory.php

language-factory.php in Polylang 3.8.6, at src/language-factory.php

342 lines 9.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 use WP_Syntex\Polylang\Options\Options;
7
8 /**
9 * PLL_Language factory.
10 *
11 * @since 3.4
12 *
13 * @phpstan-import-type LanguageData from PLL_Language
14 */
15 class PLL_Language_Factory {
16 /**
17 * Predefined languages.
18 *
19 * @var array[]
20 *
21 * @phpstan-var array<string, array<string, string>>
22 */
23 private static $languages;
24
25 /**
26 * Polylang's options.
27 *
28 * @var Options
29 */
30 private $options;
31
32 /**
33 * Constructor.
34 *
35 * @since 3.4
36 * @since 3.7 The `$options` parameter is an instance of `Options`.
37 *
38 * @param Options $options Poylang's options.
39 * @return void
40 */
41 public function __construct( Options $options ) {
42 $this->options = $options;
43 }
44
45 /**
46 * Returns a language object matching the given data, looking up in cached transient.
47 *
48 * @since 3.4
49 *
50 * @param array $language_data Language object properties stored as an array. See `PLL_Language::__construct()`
51 * for information on accepted properties.
52 *
53 * @return PLL_Language A language object if given data pass sanitization.
54 *
55 * @phpstan-param LanguageData $language_data
56 */
57 public function get( $language_data ) {
58 return new PLL_Language( $this->sanitize_data( $language_data ) );
59 }
60
61 /**
62 * Returns a language object based on terms.
63 *
64 * @since 3.4
65 *
66 * @param WP_Term[] $terms List of language terms, with the language taxonomy names as array keys.
67 * `language` is a mandatory key for the object to be created,
68 * `term_language` should be too in a fully operational environment.
69 * @return PLL_Language|null Language object on success, `null` on failure.
70 *
71 * @phpstan-param array{language?:WP_Term}&array<string, WP_Term> $terms
72 */
73 public function get_from_terms( array $terms ) {
74 if ( ! isset( $terms['language'] ) ) {
75 return null;
76 }
77
78 $languages = $this->get_languages();
79 $data = array(
80 'name' => $terms['language']->name,
81 'slug' => $terms['language']->slug,
82 'term_group' => $terms['language']->term_group,
83 'term_props' => array(),
84 'is_default' => $this->options['default_lang'] === $terms['language']->slug,
85 );
86
87 foreach ( $terms as $term ) {
88 $data['term_props'][ $term->taxonomy ] = array(
89 'term_id' => $term->term_id,
90 'term_taxonomy_id' => $term->term_taxonomy_id,
91 'count' => $term->count,
92 );
93 }
94
95 // The description fields can contain any property.
96 $description = maybe_unserialize( $terms['language']->description );
97
98 if ( is_array( $description ) ) {
99 $description = array_intersect_key(
100 $description,
101 array( 'locale' => null, 'rtl' => null, 'flag_code' => null, 'active' => null, 'fallbacks' => null )
102 );
103
104 foreach ( $description as $prop => $value ) {
105 if ( 'rtl' === $prop ) {
106 $data['is_rtl'] = $value;
107 } else {
108 $data[ $prop ] = $value;
109 }
110 }
111 }
112
113 if ( ! empty( $data['locale'] ) ) {
114 if ( isset( $languages[ $data['locale'] ]['w3c'] ) ) {
115 $data['w3c'] = $languages[ $data['locale'] ]['w3c'];
116 } else {
117 $data['w3c'] = str_replace( '_', '-', $data['locale'] );
118 }
119
120 if ( isset( $languages[ $data['locale'] ]['facebook'] ) ) {
121 $data['facebook'] = $languages[ $data['locale'] ]['facebook'];
122 }
123 }
124
125 $flag_props = $this->get_flag( $data['flag_code'], $data['name'], $data['slug'], $data['locale'] );
126 $data = array_merge( $data, $flag_props );
127
128 $additional_data = array();
129 /**
130 * Filters additional data to add to the language before it is created.
131 *
132 * `home_url`, `search_url`, `page_on_front` and `page_for_posts` are only allowed.
133 *
134 * @since 3.4
135 *
136 * @param array $additional_data.
137 * @param array $data Language data.
138 *
139 * @phpstan-param array<non-empty-string, mixed> $additional_data
140 * @phpstan-param non-empty-array<non-empty-string, mixed> $data
141 */
142 $additional_data = apply_filters( 'pll_additional_language_data', $additional_data, $data );
143
144 $allowed_additional_data = array(
145 'home_url' => '',
146 'search_url' => '',
147 'page_on_front' => 0,
148 'page_for_posts' => 0,
149 );
150
151 $data = array_merge( $data, array_intersect_key( $additional_data, $allowed_additional_data ) );
152
153 return new PLL_Language( $this->sanitize_data( $data ) );
154 }
155
156 /**
157 * Sanitizes data, to be ready to be used in the constructor.
158 * This doesn't verify that the language terms exist.
159 *
160 * @since 3.4
161 *
162 * @param array $data Data to process. See `PLL_Language::__construct()` for information on accepted data.
163 * @return array Sanitized Data.
164 *
165 * @phpstan-return LanguageData
166 */
167 private function sanitize_data( array $data ) {
168 foreach ( $data['term_props'] as $tax => $props ) {
169 $data['term_props'][ $tax ] = array_map( 'absint', $props );
170 }
171
172 $data['is_rtl'] = ! empty( $data['is_rtl'] ) ? 1 : 0;
173
174 $positive_fields = array( 'term_group', 'page_on_front', 'page_for_posts' );
175
176 foreach ( $positive_fields as $field ) {
177 $data[ $field ] = ! empty( $data[ $field ] ) ? absint( $data[ $field ] ) : 0;
178 }
179
180 $data['active'] = isset( $data['active'] ) ? (bool) $data['active'] : true;
181
182 if ( array_key_exists( 'fallbacks', $data ) && ! is_array( $data['fallbacks'] ) ) {
183 unset( $data['fallbacks'] );
184 }
185
186 /**
187 * @var LanguageData
188 */
189 return $data;
190 }
191
192 /**
193 * Returns predefined languages data.
194 *
195 * @since 3.4
196 *
197 * @return array[]
198 *
199 * @phpstan-return array<string, array<string, string>>
200 */
201 private function get_languages() {
202 if ( empty( self::$languages ) ) {
203 self::$languages = include POLYLANG_DIR . '/src/settings/languages.php';
204 }
205
206 return self::$languages;
207 }
208
209
210 /**
211 * Creates flag_url and flag language properties. Also takes care of custom flag.
212 *
213 * @since 1.2
214 * @since 3.4 Moved from `PLL_Language`to `PLL_Language_Factory` and renamed
215 * in favor of `get_flag()` (formerly `set_flag()`).
216 *
217 * @param string $flag_code Flag code.
218 * @param string $name Language name.
219 * @param string $slug Language slug.
220 * @param string $locale Language locale.
221 * @return array {
222 * Array of the flag properties.
223 * @type string $flag_url URL of the flag.
224 * @type string $flag HTML markup of the flag.
225 * @type string $custom_flag_url Optional. URL of the custom flag if it exists.
226 * @type string $custom_flag Optional. HTML markup of the custom flag if it exists.
227 * }
228 *
229 * @phpstan-return array{
230 * flag_url: string,
231 * flag: string,
232 * custom_flag_url?: non-empty-string,
233 * custom_flag?: non-empty-string
234 * }
235 */
236 private function get_flag( $flag_code, $name, $slug, $locale ) {
237 $flags = array(
238 'flag' => PLL_Language::get_flag_information( $flag_code ),
239 );
240
241 // Custom flags?
242 $directories = array(
243 PLL_LOCAL_DIR,
244 get_stylesheet_directory() . '/polylang',
245 get_template_directory() . '/polylang',
246 );
247
248 foreach ( $directories as $dir ) {
249 if ( is_readable( $file = "{$dir}/{$locale}.png" ) || is_readable( $file = "{$dir}/{$locale}.jpg" ) || is_readable( $file = "{$dir}/{$locale}.jpeg" ) || is_readable( $file = "{$dir}/{$locale}.svg" ) ) {
250 $flags['custom_flag'] = array(
251 'url' => content_url( '/' . str_replace( WP_CONTENT_DIR, '', $file ) ),
252 );
253 break;
254 }
255 }
256
257 /**
258 * Filters the custom flag information.
259 *
260 * @since 2.4
261 *
262 * @param array|null $flag {
263 * Information about the custom flag.
264 *
265 * @type string $url Flag url.
266 * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
267 * @type int $width Optional, flag width in pixels.
268 * @type int $height Optional, flag height in pixels.
269 * }
270 * @param string $code Flag code.
271 */
272 $flags['custom_flag'] = apply_filters( 'pll_custom_flag', empty( $flags['custom_flag'] ) ? null : $flags['custom_flag'], $flag_code );
273
274 if ( ! empty( $flags['custom_flag']['url'] ) ) {
275 if ( empty( $flags['custom_flag']['src'] ) ) {
276 $flags['custom_flag']['src'] = esc_url( set_url_scheme( $flags['custom_flag']['url'], 'relative' ) );
277 }
278
279 $flags['custom_flag']['url'] = sanitize_url( $flags['custom_flag']['url'] );
280 } else {
281 unset( $flags['custom_flag'] );
282 }
283
284 /**
285 * Filters the flag title attribute.
286 * Defaults to the language name.
287 *
288 * @since 0.7
289 *
290 * @param string $title The flag title attribute.
291 * @param string $slug The language code.
292 * @param string $locale The language locale.
293 */
294 $title = apply_filters( 'pll_flag_title', $name, $slug, $locale );
295 $return = array();
296
297 /**
298 * @var array{
299 * flag: array{
300 * url: string,
301 * src: string,
302 * width?: positive-int,
303 * height?: positive-int
304 * },
305 * custom_flag?: array{
306 * url: non-empty-string,
307 * src: non-empty-string,
308 * width?: positive-int,
309 * height?: positive-int
310 * }
311 * } $flags
312 */
313 foreach ( $flags as $key => $flag ) {
314 $return[ "{$key}_url" ] = $flag['url'];
315
316 /**
317 * Filters the html markup of a flag.
318 *
319 * @since 1.0.2
320 *
321 * @param string $flag Html markup of the flag or empty string.
322 * @param string $slug Language code.
323 */
324 $return[ $key ] = apply_filters(
325 'pll_get_flag',
326 PLL_Language::get_flag_html( $flag, $title, $name ),
327 $slug
328 );
329 }
330
331 /**
332 * @var array{
333 * flag_url: string,
334 * flag: string,
335 * custom_flag_url?: non-empty-string,
336 * custom_flag?: non-empty-string
337 * } $return
338 */
339 return $return;
340 }
341 }
342