PluginProbe
Polylang / 3.4
Polylang v3.4
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 / include / language.php

language.php in Polylang 3.4, at include/language.php

655 lines 17.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 * A language object is made of two terms in 'language' and 'term_language' taxonomies.
8 * Manipulating only one object per language instead of two terms should make things easier.
9 *
10 * @since 1.2
11 * @immutable
12 *
13 * @phpstan-type LanguagePropData array{
14 * term_id: positive-int,
15 * term_taxonomy_id: positive-int,
16 * count: int<0, max>
17 * }
18 * @phpstan-type LanguageData array{
19 * term_props: array{
20 * language: LanguagePropData,
21 * }&array<non-empty-string, LanguagePropData>,
22 * name: non-empty-string,
23 * slug: non-empty-string,
24 * locale: non-empty-string,
25 * w3c: non-empty-string,
26 * flag_code: non-empty-string,
27 * term_group: int,
28 * is_rtl: int<0, 1>,
29 * facebook?: string,
30 * home_url: non-empty-string,
31 * search_url: non-empty-string,
32 * host: non-empty-string,
33 * flag_url: non-empty-string,
34 * flag: non-empty-string,
35 * custom_flag_url?: string,
36 * custom_flag?: string,
37 * page_on_front: int<0, max>,
38 * page_for_posts: int<0, max>,
39 * active: bool,
40 * fallbacks?: array<non-empty-string>,
41 * is_default: bool
42 * }
43 */
44 class PLL_Language extends PLL_Language_Deprecated {
45
46 /**
47 * Language name. Ex: English.
48 *
49 * @var string
50 *
51 * @phpstan-var non-empty-string
52 */
53 public $name;
54
55 /**
56 * Language code used in URL. Ex: en.
57 *
58 * @var string
59 *
60 * @phpstan-var non-empty-string
61 */
62 public $slug;
63
64 /**
65 * Order of the language when displayed in a list of languages.
66 *
67 * @var int
68 */
69 public $term_group;
70
71 /**
72 * ID of the term in 'language' taxonomy.
73 * Duplicated from `$this->term_props['language']['term_id'],
74 * but kept to facilitate the use of it.
75 *
76 * @var int
77 *
78 * @phpstan-var int<1, max>
79 */
80 public $term_id;
81
82 /**
83 * WordPress language locale. Ex: en_US.
84 *
85 * @var string
86 *
87 * @phpstan-var non-empty-string
88 */
89 public $locale;
90
91 /**
92 * 1 if the language is rtl, 0 otherwise.
93 *
94 * @var int
95 *
96 * @phpstan-var int<0, 1>
97 */
98 public $is_rtl;
99
100 /**
101 * W3C locale.
102 *
103 * @var string
104 *
105 * @phpstan-var non-empty-string
106 */
107 public $w3c;
108
109 /**
110 * Facebook locale.
111 *
112 * @var string
113 */
114 public $facebook = '';
115
116 /**
117 * Home URL in this language.
118 *
119 * @var string
120 *
121 * @phpstan-var non-empty-string
122 */
123 private $home_url;
124
125 /**
126 * Home URL to use in search forms.
127 *
128 * @var string
129 *
130 * @phpstan-var non-empty-string
131 */
132 private $search_url;
133
134 /**
135 * Host corresponding to this language.
136 *
137 * @var string
138 *
139 * @phpstan-var non-empty-string
140 */
141 public $host;
142
143 /**
144 * ID of the page on front in this language (set from pll_additional_language_data filter).
145 *
146 * @var int
147 *
148 * @phpstan-var int<0, max>
149 */
150 public $page_on_front = 0;
151
152 /**
153 * ID of the page for posts in this language (set from pll_additional_language_data filter).
154 *
155 * @var int
156 *
157 * @phpstan-var int<0, max>
158 */
159 public $page_for_posts = 0;
160
161 /**
162 * Code of the flag.
163 *
164 * @var string
165 *
166 * @phpstan-var non-empty-string
167 */
168 public $flag_code;
169
170 /**
171 * URL of the flag. Always set to the main domain.
172 *
173 * @var string
174 *
175 * @phpstan-var non-empty-string
176 */
177 public $flag_url;
178
179 /**
180 * HTML markup of the flag.
181 *
182 * @var string
183 *
184 * @phpstan-var non-empty-string
185 */
186 public $flag;
187
188 /**
189 * URL of the custom flag if it exists. Always set to the main domain.
190 *
191 * @var string
192 */
193 public $custom_flag_url = '';
194
195 /**
196 * HTML markup of the custom flag if it exists.
197 *
198 * @var string
199 */
200 public $custom_flag = '';
201
202 /**
203 * Whether or not the language is active. Default `true`.
204 *
205 * @var bool
206 */
207 public $active = true;
208
209 /**
210 * List of WordPress language locales. Ex: array( 'en_GB' ).
211 *
212 * @var string[]
213 *
214 * @phpstan-var list<non-empty-string>
215 */
216 public $fallbacks = array();
217
218 /**
219 * Whether the language is the default one.
220 *
221 * @var bool
222 */
223 public $is_default;
224
225 /**
226 * Stores language term properties (like term IDs and counts) for each language taxonomy (`language`,
227 * `term_language`, etc).
228 * This stores the values of the properties `$term_id` + `$term_taxonomy_id` + `$count` (`language`), `$tl_term_id`
229 * + `$tl_term_taxonomy_id` + `$tl_count` (`term_language`), and the `term_id` + `term_taxonomy_id` + `count` for
230 * other language taxonomies.
231 *
232 * @var array[] Array keys are language term names.
233 *
234 * @exemple array(
235 * 'language' => array(
236 * 'term_id' => 7,
237 * 'term_taxonomy_id' => 8,
238 * 'count' => 11,
239 * ),
240 * 'term_language' => array(
241 * 'term_id' => 11,
242 * 'term_taxonomy_id' => 12,
243 * 'count' => 6,
244 * ),
245 * 'foo_language' => array(
246 * 'term_id' => 33,
247 * 'term_taxonomy_id' => 34,
248 * 'count' => 0,
249 * ),
250 * )
251 *
252 * @phpstan-var array{
253 * language: LanguagePropData,
254 * }
255 * &array<non-empty-string, LanguagePropData>
256 */
257 protected $term_props;
258
259 /**
260 * Constructor: builds a language object given the corresponding data.
261 *
262 * @since 1.2
263 * @since 3.4 Only accepts one argument.
264 *
265 * @param array $language_data {
266 * Language object properties stored as an array.
267 *
268 * @type array[] $term_props An array of language term properties. Array keys are language taxonomy names
269 * (`language` and `term_language` are mandatory), array values are arrays of
270 * language term properties (`term_id`, `term_taxonomy_id`, and `count`).
271 * @type string $name Language name. Ex: English.
272 * @type string $slug Language code used in URL. Ex: en.
273 * @type string $locale WordPress language locale. Ex: en_US.
274 * @type string $w3c W3C locale.
275 * @type string $flag_code Code of the flag.
276 * @type int $term_group Order of the language when displayed in a list of languages.
277 * @type int $is_rtl `1` if the language is rtl, `0` otherwise.
278 * @type string $facebook Optional. Facebook locale.
279 * @type string $home_url Home URL in this language.
280 * @type string $search_url Home URL to use in search forms.
281 * @type string $host Host corresponding to this language.
282 * @type string $flag_url URL of the flag.
283 * @type string $flag HTML markup of the flag.
284 * @type string $custom_flag_url Optional. URL of the custom flag if it exists.
285 * @type string $custom_flag Optional. HTML markup of the custom flag if it exists.
286 * @type int $page_on_front Optional. ID of the page on front in this language.
287 * @type int $page_for_posts Optional. ID of the page for posts in this language.
288 * @type bool $active Whether or not the language is active. Default `true`.
289 * @type string[] $fallbacks List of WordPress language locales. Ex: array( 'en_GB' ).
290 * @type bool $is_default Whether or not the language is the default one.
291 * }
292 *
293 * @phpstan-param LanguageData $language_data
294 */
295 public function __construct( array $language_data ) {
296 foreach ( $language_data as $prop => $value ) {
297 $this->$prop = $value;
298 }
299
300 $this->term_id = $this->term_props['language']['term_id'];
301 }
302
303 /**
304 * Returns a language term property value (term ID, term taxonomy ID, or count).
305 *
306 * @since 3.4
307 *
308 * @param string $taxonomy_name Name of the taxonomy.
309 * @param string $prop_name Name of the property: 'term_taxonomy_id', 'term_id', 'count'.
310 * @return int
311 *
312 * @phpstan-param non-empty-string $taxonomy_name
313 * @phpstan-param 'term_taxonomy_id'|'term_id'|'count' $prop_name
314 * @phpstan-return int<0, max>
315 */
316 public function get_tax_prop( $taxonomy_name, $prop_name ) {
317 return isset( $this->term_props[ $taxonomy_name ][ $prop_name ] ) ? $this->term_props[ $taxonomy_name ][ $prop_name ] : 0;
318 }
319
320 /**
321 * Returns the language term props for all content types.
322 *
323 * @since 3.4
324 *
325 * @param string $property Name of the field to return. An empty string to return them all.
326 * @return (int[]|int)[] Array keys are taxonomy names, array values depend of `$property`.
327 *
328 * @phpstan-param 'term_taxonomy_id'|'term_id'|'count'|'' $property
329 * @phpstan-return array<non-empty-string, (
330 * $property is non-empty-string ?
331 * (
332 * $property is 'count' ?
333 * int<0, max> :
334 * positive-int
335 * ) :
336 * LanguagePropData
337 * )>
338 */
339 public function get_tax_props( $property = '' ) {
340 if ( empty( $property ) ) {
341 return $this->term_props;
342 }
343
344 $term_props = array();
345
346 foreach ( $this->term_props as $taxonomy_name => $props ) {
347 $term_props[ $taxonomy_name ] = $props[ $property ];
348 }
349
350 return $term_props;
351 }
352
353 /**
354 * Returns the flag informations.
355 *
356 * @since 2.6
357 *
358 * @param string $code Flag code.
359 * @return array {
360 * Flag informations.
361 *
362 * @type string $url Flag url.
363 * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
364 * @type int $width Optional, flag width in pixels.
365 * @type int $height Optional, flag height in pixels.
366 * }
367 *
368 * @phpstan-return array{
369 * url: string,
370 * src: string,
371 * width?: positive-int,
372 * height?: positive-int
373 * }
374 */
375 public static function get_flag_informations( $code ) {
376 $flag = array( 'url' => '' );
377
378 // Polylang builtin flags.
379 if ( ! empty( $code ) && is_readable( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
380 $flag['url'] = plugins_url( $file, POLYLANG_FILE );
381
382 // If base64 encoded flags are preferred.
383 if ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) {
384 $imagesize = getimagesize( POLYLANG_DIR . $file );
385 if ( is_array( $imagesize ) ) {
386 list( $flag['width'], $flag['height'] ) = $imagesize;
387 }
388 $file_contents = file_get_contents( POLYLANG_DIR . $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
389 $flag['src'] = 'data:image/png;base64,' . base64_encode( $file_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
390 }
391 }
392
393 /**
394 * Filters flag informations:
395 *
396 * @since 2.4
397 *
398 * @param array $flag {
399 * Information about the flag.
400 *
401 * @type string $url Flag url.
402 * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
403 * @type int $width Optional, flag width in pixels.
404 * @type int $height Optional, flag height in pixels.
405 * }
406 * @param string $code Flag code.
407 */
408 $flag = apply_filters( 'pll_flag', $flag, $code );
409
410 $flag['url'] = esc_url_raw( $flag['url'] );
411
412 if ( empty( $flag['src'] ) ) {
413 $flag['src'] = esc_url( set_url_scheme( $flag['url'], 'relative' ) );
414 }
415
416 return $flag;
417 }
418
419 /**
420 * Returns HTML code for flag.
421 *
422 * @since 2.7
423 *
424 * @param array $flag Flag properties: src, width and height.
425 * @param string $title Optional title attribute.
426 * @param string $alt Optional alt attribute.
427 * @return string
428 *
429 * @phpstan-param array{
430 * src: string,
431 * width?: int|numeric-string,
432 * height?: int|numeric-string
433 * } $flag
434 */
435 public static function get_flag_html( $flag, $title = '', $alt = '' ) {
436 if ( empty( $flag['src'] ) ) {
437 return '';
438 }
439
440 $alt_attr = empty( $alt ) ? '' : sprintf( ' alt="%s"', esc_attr( $alt ) );
441 $width_attr = empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] );
442 $height_attr = empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] );
443
444 $style = '';
445 $sizes = array_intersect_key( $flag, array_flip( array( 'width', 'height' ) ) );
446
447 if ( ! empty( $sizes ) ) {
448 array_walk(
449 $sizes,
450 function ( &$value, $key ) {
451 $value = sprintf( '%s: %dpx;', esc_attr( $key ), (int) $value );
452 }
453 );
454 $style = sprintf( ' style="%s"', implode( ' ', $sizes ) );
455 }
456
457 return sprintf(
458 '<img src="%s"%s%s%s%s />',
459 $flag['src'],
460 $alt_attr,
461 $width_attr,
462 $height_attr,
463 $style
464 );
465 }
466
467 /**
468 * Returns the html of the custom flag if any, or the default flag otherwise.
469 *
470 * @since 2.8
471 *
472 * @return string
473 */
474 public function get_display_flag() {
475 return empty( $this->custom_flag ) ? $this->flag : $this->custom_flag;
476 }
477
478 /**
479 * Returns the url of the custom flag if any, or the default flag otherwise.
480 *
481 * @since 2.8
482 *
483 * @return string
484 */
485 public function get_display_flag_url() {
486 $flag_url = empty( $this->custom_flag_url ) ? $this->flag_url : $this->custom_flag_url;
487
488 /**
489 * Let's use `site_url()` so the returned URL will be filtered properly according to the current domain.
490 */
491 return site_url( set_url_scheme( $flag_url, 'relative' ) );
492 }
493
494 /**
495 * Updates post and term count.
496 *
497 * @since 1.2
498 *
499 * @return void
500 */
501 public function update_count() {
502 foreach ( $this->term_props as $taxonomy => $props ) {
503 wp_update_term_count( $props['term_taxonomy_id'], $taxonomy );
504 }
505 }
506
507 /**
508 * Returns the language locale.
509 * Converts WP locales to W3C valid locales for display.
510 *
511 * @since 1.8
512 *
513 * @param string $filter Either 'display' or 'raw', defaults to raw.
514 * @return string
515 *
516 * @phpstan-param 'display'|'raw' $filter
517 * @phpstan-return non-empty-string
518 */
519 public function get_locale( $filter = 'raw' ) {
520 return 'display' === $filter ? $this->w3c : $this->locale;
521 }
522
523 /**
524 * Returns the values of this instance's properties, which can be filtered if required.
525 *
526 * @since 3.4
527 *
528 * @param string $context Whether or not properties should be filtered. Accepts `db` or `display`.
529 * Default to `display` which filters some properties.
530 *
531 * @return array Array of language object properties.
532 *
533 * @phpstan-return LanguageData
534 */
535 public function to_array( $context = 'display' ) {
536 $language = get_object_vars( $this );
537
538 if ( 'db' !== $context ) {
539 $language['home_url'] = $this->get_home_url();
540 $language['search_url'] = $this->get_search_url();
541 }
542
543 /** @phpstan-var LanguageData $language */
544 return $language;
545 }
546
547 /**
548 * Converts current `PLL_language` into a `stdClass` object. Mostly used to allow dynamic properties.
549 *
550 * @since 3.4
551 *
552 * @return stdClass Converted `PLL_Language` object.
553 */
554 public function to_std_class() {
555 return (object) $this->to_array();
556 }
557
558 /**
559 * Returns a predefined HTML flag.
560 *
561 * @since 3.4
562 *
563 * @param string $flag_code Flag code to render.
564 * @return string HTML code for the flag.
565 */
566 public static function get_predefined_flag( $flag_code ) {
567 $flag = self::get_flag_informations( $flag_code );
568
569 return self::get_flag_html( $flag );
570 }
571
572 /**
573 * Returns language's home URL. Takes care to render it dynamically if no cache is allowed.
574 *
575 * @since 3.4
576 *
577 * @return string Language home URL.
578 *
579 * @phpstan-return non-empty-string
580 */
581 public function get_home_url() {
582 if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || ( defined( 'PLL_CACHE_HOME_URL' ) && ! PLL_CACHE_HOME_URL ) ) {
583 /**
584 * Let's use `site_url()` so the returned URL will be filtered properly according to the current domain.
585 *
586 * @phpstan-var non-empty-string
587 */
588 return site_url( set_url_scheme( $this->home_url, 'relative' ) );
589 }
590
591 return $this->home_url;
592 }
593
594 /**
595 * Returns language's search URL. Takes care to render it dynamically if no cache is allowed.
596 *
597 * @since 3.4
598 *
599 * @return string Language search URL.
600 *
601 * @phpstan-return non-empty-string
602 */
603 public function get_search_url() {
604 if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || ( defined( 'PLL_CACHE_HOME_URL' ) && ! PLL_CACHE_HOME_URL ) ) {
605 /**
606 * Let's use `site_url()` so the returned URL will be filtered properly according to the current domain.
607 *
608 * @phpstan-var non-empty-string
609 */
610 return site_url( set_url_scheme( $this->search_url, 'relative' ) );
611 }
612
613 return $this->search_url;
614 }
615
616 /**
617 * Returns the value of a language property.
618 * This is handy to get a property's value without worrying about triggering a deprecation warning or anything.
619 *
620 * @since 3.4
621 *
622 * @param string $property A property name. A composite value can be used for language term property values, in the
623 * form of `{language_taxonomy_name}:{property_name}` (see {@see PLL_Language::get_tax_prop()}
624 * for the possible values). Ex: `term_language:term_taxonomy_id`.
625 * @return string|int|bool|string[] The requested property for the language, `false` if the property doesn't exist.
626 *
627 * @phpstan-return (
628 * $property is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
629 * )
630 */
631 public function get_prop( $property ) {
632 // Deprecated property.
633 if ( $this->is_deprecated_term_property( $property ) ) {
634 return $this->get_deprecated_term_property( $property );
635 }
636
637 if ( $this->is_deprecated_url_property( $property ) ) {
638 return $this->get_deprecated_url_property( $property );
639 }
640
641 // Composite property like 'term_language:term_taxonomy_id'.
642 if ( preg_match( '/^(?<tax>.{1,32}):(?<field>term_id|term_taxonomy_id|count)$/', $property, $matches ) ) {
643 /** @var array{tax:non-empty-string, field:'term_id'|'term_taxonomy_id'|'count'} $matches */
644 return $this->get_tax_prop( $matches['tax'], $matches['field'] );
645 }
646
647 // Any other public property.
648 if ( isset( $this->$property ) ) {
649 return $this->$property;
650 }
651
652 return false;
653 }
654 }
655