PluginProbe
Polylang / 3.8.9
Polylang v3.8.9
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.php

language.php in Polylang 3.8.9, at src/language.php

679 lines 18.1 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 * @example 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 $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 information.
355 *
356 * @since 2.6
357 *
358 * @param string $code Flag code.
359 * @return array {
360 * Flag information.
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_information( $code ) {
376 $default_flag = array(
377 'url' => '',
378 'src' => '',
379 );
380
381 // Polylang builtin flags.
382 if ( ! empty( $code ) && is_readable( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
383 $default_flag['url'] = plugins_url( $file, POLYLANG_FILE );
384
385 // If base64 encoded flags are preferred.
386 if ( pll_get_constant( 'PLL_ENCODED_FLAGS', true ) ) {
387 $imagesize = getimagesize( POLYLANG_DIR . $file );
388 if ( is_array( $imagesize ) ) {
389 list( $default_flag['width'], $default_flag['height'] ) = $imagesize;
390 }
391 $file_contents = file_get_contents( POLYLANG_DIR . $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
392 $default_flag['src'] = 'data:image/png;base64,' . base64_encode( $file_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
393 }
394 }
395
396 /**
397 * Filters flag information:
398 *
399 * @since 2.4
400 *
401 * @param array $flag {
402 * Information about the flag.
403 *
404 * @type string $url Flag url.
405 * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
406 * @type int $width Optional, flag width in pixels.
407 * @type int $height Optional, flag height in pixels.
408 * }
409 * @param string $code Flag code.
410 */
411 $flag = apply_filters( 'pll_flag', $default_flag, $code );
412
413 $flag['url'] = sanitize_url( $flag['url'] );
414
415 if ( empty( $flag['src'] ) || ( $flag['src'] === $default_flag['src'] && $flag['url'] !== $default_flag['url'] ) ) {
416 $flag['src'] = esc_url( set_url_scheme( $flag['url'], 'relative' ) );
417 }
418
419 if ( isset( $flag['width'] ) ) {
420 $flag['width'] = absint( $flag['width'] );
421 }
422
423 if ( isset( $flag['height'] ) ) {
424 $flag['height'] = absint( $flag['height'] );
425 }
426
427 return $flag;
428 }
429
430 /**
431 * Returns HTML code for flag.
432 *
433 * @since 2.7
434 *
435 * @param array $flag Flag properties: src, width and height.
436 * @param string $title Optional title attribute.
437 * @param string $alt Optional alt attribute.
438 * @return string
439 *
440 * @phpstan-param array{
441 * src: string,
442 * width?: int|numeric-string,
443 * height?: int|numeric-string
444 * } $flag
445 */
446 public static function get_flag_html( $flag, $title = '', $alt = '' ) {
447 if ( empty( $flag['src'] ) ) {
448 return '';
449 }
450
451 $alt_attr = empty( $alt ) ? '' : sprintf( ' alt="%s"', esc_attr( $alt ) );
452 $width_attr = empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] );
453 $height_attr = empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] );
454
455 $style = '';
456 $sizes = array_intersect_key( $flag, array_flip( array( 'width', 'height' ) ) );
457
458 if ( ! empty( $sizes ) ) {
459 array_walk(
460 $sizes,
461 function ( &$value, $key ) {
462 $value = sprintf( '%s: %dpx;', esc_attr( $key ), (int) $value );
463 }
464 );
465 $style = sprintf( ' style="%s"', implode( ' ', $sizes ) );
466 }
467
468 return sprintf(
469 '<img src="%s"%s%s%s%s />',
470 $flag['src'],
471 $alt_attr,
472 $width_attr,
473 $height_attr,
474 $style
475 );
476 }
477
478 /**
479 * Returns the html of the custom flag if any, or the default flag otherwise.
480 *
481 * @since 2.8
482 * @since 3.5.3 Added the `$alt` parameter.
483 *
484 * @param string $alt Whether or not the alternative text should be set. Accepts 'alt' and 'no-alt'.
485 *
486 * @return string
487 *
488 * @phpstan-param 'alt'|'no-alt' $alt
489 */
490 public function get_display_flag( $alt = 'alt' ) {
491 $flag = empty( $this->custom_flag ) ? $this->flag : $this->custom_flag;
492
493 if ( 'alt' === $alt ) {
494 return $flag;
495 }
496
497 return (string) preg_replace( '/(?<=\salt=\")([^"]+)(?=\")/', '', $flag );
498 }
499
500 /**
501 * Returns the url of the custom flag if any, or the default flag otherwise.
502 *
503 * @since 2.8
504 *
505 * @return string
506 */
507 public function get_display_flag_url() {
508 $flag_url = empty( $this->custom_flag_url ) ? $this->flag_url : $this->custom_flag_url;
509
510 /**
511 * Filters `flag_url` property.
512 *
513 * @since 3.4.4
514 *
515 * @param string $flag_url Flag URL.
516 * @param PLL_Language $language Current `PLL_language` instance.
517 */
518 return apply_filters( 'pll_language_flag_url', $flag_url, $this );
519 }
520
521 /**
522 * Updates post and term count.
523 *
524 * @since 1.2
525 *
526 * @return void
527 */
528 public function update_count() {
529 foreach ( $this->term_props as $taxonomy => $props ) {
530 wp_update_term_count( $props['term_taxonomy_id'], $taxonomy );
531 }
532 }
533
534 /**
535 * Returns the language locale.
536 * Converts WP locales to W3C valid locales for display.
537 *
538 * @since 1.8
539 *
540 * @param string $filter Either 'display' or 'raw', defaults to raw.
541 * @return string
542 *
543 * @phpstan-param 'display'|'raw' $filter
544 * @phpstan-return non-empty-string
545 */
546 public function get_locale( $filter = 'raw' ) {
547 return 'display' === $filter ? $this->w3c : $this->locale;
548 }
549
550 /**
551 * Returns the values of this instance's properties, which can be filtered if required.
552 *
553 * @since 3.4
554 *
555 * @param string $context Whether or not properties should be filtered. Accepts `db` or `display`.
556 * Default to `display` which filters some properties.
557 *
558 * @return array Array of language object properties.
559 *
560 * @phpstan-return LanguageData
561 */
562 public function to_array( $context = 'display' ) {
563 $language = get_object_vars( $this );
564
565 if ( 'db' !== $context ) {
566 $language['home_url'] = $this->get_home_url();
567 $language['search_url'] = $this->get_search_url();
568 }
569
570 /** @phpstan-var LanguageData $language */
571 return $language;
572 }
573
574 /**
575 * Converts current `PLL_language` into a `stdClass` object. Mostly used to allow dynamic properties.
576 *
577 * @since 3.4
578 *
579 * @return stdClass Converted `PLL_Language` object.
580 */
581 public function to_std_class() {
582 return (object) $this->to_array();
583 }
584
585 /**
586 * Returns a predefined HTML flag.
587 *
588 * @since 3.4
589 *
590 * @param string $flag_code Flag code to render.
591 * @return string HTML code for the flag.
592 */
593 public static function get_predefined_flag( $flag_code ) {
594 $flag = self::get_flag_information( $flag_code );
595
596 return self::get_flag_html( $flag );
597 }
598
599 /**
600 * Returns language's home URL. Takes care to render it dynamically if no cache is allowed.
601 *
602 * @since 3.4
603 *
604 * @return string Language home URL.
605 */
606 public function get_home_url() {
607 if ( ! pll_get_constant( 'PLL_CACHE_LANGUAGES', true ) || ! pll_get_constant( 'PLL_CACHE_HOME_URL', true ) ) {
608 /**
609 * Filters current `PLL_Language` instance `home_url` property.
610 *
611 * @since 3.4.4
612 *
613 * @param string $home_url The `home_url` prop.
614 * @param array $language Current Array of `PLL_Language` properties.
615 */
616 return apply_filters( 'pll_language_home_url', $this->home_url, $this->to_array( 'db' ) );
617 }
618
619 return $this->home_url;
620 }
621
622 /**
623 * Returns language's search URL. Takes care to render it dynamically if no cache is allowed.
624 *
625 * @since 3.4
626 *
627 * @return string Language search URL.
628 */
629 public function get_search_url() {
630 if ( ! pll_get_constant( 'PLL_CACHE_LANGUAGES', true ) || ! pll_get_constant( 'PLL_CACHE_HOME_URL', true ) ) {
631 /**
632 * Filters current `PLL_Language` instance `search_url` property.
633 *
634 * @since 3.4.4
635 *
636 * @param string $search_url The `search_url` prop.
637 * @param array $language Current Array of `PLL_Language` properties.
638 */
639 return apply_filters( 'pll_language_search_url', $this->search_url, $this->to_array( 'db' ) );
640 }
641
642 return $this->search_url;
643 }
644
645 /**
646 * Returns the value of a language property.
647 * This is handy to get a property's value without worrying about triggering a deprecation warning or anything.
648 *
649 * @since 3.4
650 *
651 * @param string $property A property name. A composite value can be used for language term property values, in the
652 * form of `{language_taxonomy_name}:{property_name}` (see {@see PLL_Language::get_tax_prop()}
653 * for the possible values). Ex: `term_language:term_taxonomy_id`.
654 * @return string|int|bool|string[] The requested property for the language, `false` if the property doesn't exist.
655 *
656 * @phpstan-return (
657 * $property is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
658 * )
659 */
660 public function get_prop( $property ) {
661 // Deprecated property.
662 if ( $this->is_deprecated_term_property( $property ) ) {
663 return $this->get_deprecated_term_property( $property );
664 }
665
666 if ( $this->is_deprecated_url_property( $property ) ) {
667 return $this->get_deprecated_url_property( $property );
668 }
669
670 // Composite property like 'term_language:term_taxonomy_id'.
671 if ( preg_match( '/^(?<tax>.{1,32}):(?<field>term_id|term_taxonomy_id|count)$/', $property, $matches ) ) {
672 /** @var array{tax:non-empty-string, field:'term_id'|'term_taxonomy_id'|'count'} $matches */
673 return $this->get_tax_prop( $matches['tax'], $matches['field'] );
674 }
675
676 return $this->$property ?? false;
677 }
678 }
679