PluginProbe
Polylang / 3.5.1
Polylang v3.5.1
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.5.1, at include/language.php

662 lines 17.6 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 ( pll_get_constant( 'PLL_ENCODED_FLAGS', true ) ) {
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 * Filters `flag_url` property.
490 *
491 * @since 3.4.4
492 *
493 * @param string $flag_url Flag URL.
494 * @param PLL_Language $language Current `PLL_language` instance.
495 */
496 return apply_filters( 'pll_language_flag_url', $flag_url, $this );
497 }
498
499 /**
500 * Updates post and term count.
501 *
502 * @since 1.2
503 *
504 * @return void
505 */
506 public function update_count() {
507 foreach ( $this->term_props as $taxonomy => $props ) {
508 wp_update_term_count( $props['term_taxonomy_id'], $taxonomy );
509 }
510 }
511
512 /**
513 * Returns the language locale.
514 * Converts WP locales to W3C valid locales for display.
515 *
516 * @since 1.8
517 *
518 * @param string $filter Either 'display' or 'raw', defaults to raw.
519 * @return string
520 *
521 * @phpstan-param 'display'|'raw' $filter
522 * @phpstan-return non-empty-string
523 */
524 public function get_locale( $filter = 'raw' ) {
525 return 'display' === $filter ? $this->w3c : $this->locale;
526 }
527
528 /**
529 * Returns the values of this instance's properties, which can be filtered if required.
530 *
531 * @since 3.4
532 *
533 * @param string $context Whether or not properties should be filtered. Accepts `db` or `display`.
534 * Default to `display` which filters some properties.
535 *
536 * @return array Array of language object properties.
537 *
538 * @phpstan-return LanguageData
539 */
540 public function to_array( $context = 'display' ) {
541 $language = get_object_vars( $this );
542
543 if ( 'db' !== $context ) {
544 $language['home_url'] = $this->get_home_url();
545 $language['search_url'] = $this->get_search_url();
546 }
547
548 /** @phpstan-var LanguageData $language */
549 return $language;
550 }
551
552 /**
553 * Converts current `PLL_language` into a `stdClass` object. Mostly used to allow dynamic properties.
554 *
555 * @since 3.4
556 *
557 * @return stdClass Converted `PLL_Language` object.
558 */
559 public function to_std_class() {
560 return (object) $this->to_array();
561 }
562
563 /**
564 * Returns a predefined HTML flag.
565 *
566 * @since 3.4
567 *
568 * @param string $flag_code Flag code to render.
569 * @return string HTML code for the flag.
570 */
571 public static function get_predefined_flag( $flag_code ) {
572 $flag = self::get_flag_informations( $flag_code );
573
574 return self::get_flag_html( $flag );
575 }
576
577 /**
578 * Returns language's home URL. Takes care to render it dynamically if no cache is allowed.
579 *
580 * @since 3.4
581 *
582 * @return string Language home URL.
583 */
584 public function get_home_url() {
585 if ( ! pll_get_constant( 'PLL_CACHE_LANGUAGES', true ) || ! pll_get_constant( 'PLL_CACHE_HOME_URL', true ) ) {
586 /**
587 * Filters current `PLL_Language` instance `home_url` property.
588 *
589 * @since 3.4.4
590 *
591 * @param string $home_url The `home_url` prop.
592 * @param array $language Current Array of `PLL_Language` properties.
593 */
594 return apply_filters( 'pll_language_home_url', $this->home_url, $this->to_array( 'db' ) );
595 }
596
597 return $this->home_url;
598 }
599
600 /**
601 * Returns language's search URL. Takes care to render it dynamically if no cache is allowed.
602 *
603 * @since 3.4
604 *
605 * @return string Language search URL.
606 */
607 public function get_search_url() {
608 if ( ! pll_get_constant( 'PLL_CACHE_LANGUAGES', true ) || ! pll_get_constant( 'PLL_CACHE_HOME_URL', true ) ) {
609 /**
610 * Filters current `PLL_Language` instance `search_url` property.
611 *
612 * @since 3.4.4
613 *
614 * @param string $search_url The `search_url` prop.
615 * @param array $language Current Array of `PLL_Language` properties.
616 */
617 return apply_filters( 'pll_language_search_url', $this->search_url, $this->to_array( 'db' ) );
618 }
619
620 return $this->search_url;
621 }
622
623 /**
624 * Returns the value of a language property.
625 * This is handy to get a property's value without worrying about triggering a deprecation warning or anything.
626 *
627 * @since 3.4
628 *
629 * @param string $property A property name. A composite value can be used for language term property values, in the
630 * form of `{language_taxonomy_name}:{property_name}` (see {@see PLL_Language::get_tax_prop()}
631 * for the possible values). Ex: `term_language:term_taxonomy_id`.
632 * @return string|int|bool|string[] The requested property for the language, `false` if the property doesn't exist.
633 *
634 * @phpstan-return (
635 * $property is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
636 * )
637 */
638 public function get_prop( $property ) {
639 // Deprecated property.
640 if ( $this->is_deprecated_term_property( $property ) ) {
641 return $this->get_deprecated_term_property( $property );
642 }
643
644 if ( $this->is_deprecated_url_property( $property ) ) {
645 return $this->get_deprecated_url_property( $property );
646 }
647
648 // Composite property like 'term_language:term_taxonomy_id'.
649 if ( preg_match( '/^(?<tax>.{1,32}):(?<field>term_id|term_taxonomy_id|count)$/', $property, $matches ) ) {
650 /** @var array{tax:non-empty-string, field:'term_id'|'term_taxonomy_id'|'count'} $matches */
651 return $this->get_tax_prop( $matches['tax'], $matches['field'] );
652 }
653
654 // Any other public property.
655 if ( isset( $this->$property ) ) {
656 return $this->$property;
657 }
658
659 return false;
660 }
661 }
662