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

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

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