PluginProbe
Polylang / 3.7.1
Polylang v3.7.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-factory.php

language-factory.php in Polylang 3.7.1, at include/language-factory.php

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