PluginProbe
Polylang / trunk
Polylang vtrunk
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 / model.php

model.php in Polylang trunk, at src/model.php

665 lines 24.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 use WP_Syntex\Polylang\Model;
7 use WP_Syntex\Polylang\Options\Options;
8
9 /**
10 * Setups the language and translations model based on WordPress taxonomies.
11 *
12 * @since 1.2
13 *
14 * @method bool has_languages() Checks if there are languages or not. See `Model\Languages::has()`.
15 * @method array get_languages_list(array $args = array()) Returns the list of available languages. See `Model\Languages::get_list()`.
16 * @method bool are_languages_ready() Tells if get_languages_list() can be used. See `Model\Languages::are_ready()`.
17 * @method void set_languages_ready() Sets the internal property `$languages_ready` to `true`, telling that get_languages_list() can be used. See `Model\Languages::set_ready()`.
18 * @method PLL_Language|false get_language(mixed $value) Returns the language by its term_id, tl_term_id, slug or locale. See `Model\Languages::get()`.
19 * @method PLL_Language|WP_Error add_language(array $args) Adds a new language and creates a default category for this language. See `Model\Languages::add()`.
20 * @method bool delete_language(int $lang_id) Deletes a language. See `Model\Languages::delete()`.
21 * @method PLL_Language|WP_Error update_language(array $args) Updates language properties. See `Model\Languages::update()`.
22 * @method PLL_Language|false get_default_language() Returns the default language. See `Model\Languages::get_default()`.
23 * @method void update_default_lang(string $slug) Updates the default language. See `Model\Languages::update_default()`.
24 * @method void maybe_create_language_terms() Maybe adds the missing language terms for 3rd party language taxonomies. See `Model\Languages::maybe_create_terms()`.
25 * @method string[] get_translated_post_types(bool $filter = true) Returns post types that need to be translated. See `Model\Post_Types::get_translated()`.
26 * @method bool is_translated_post_type(string|string[] $post_type) Returns true if Polylang manages languages and translations for this post type. See `Model\Post_Types::is_translated()`.
27 * @method string[] get_translated_taxonomies(bool $filter = true) Returns taxonomies that need to be translated. See `Model\Taxonomies::get_translated()`.
28 * @method bool is_translated_taxonomy(string|string[] $tax) Returns true if Polylang manages languages and translations for this taxonomy. See `Model\Taxonomies::is_translated()`.
29 * @method string[] get_filtered_taxonomies(bool $filter = true) Return taxonomies that need to be filtered (post_format like). See `Model\Taxonomies::get_filtered()`.
30 * @method bool is_filtered_taxonomy(string|string[] $tax) Returns true if Polylang filters this taxonomy per language. See `Model\Taxonomies::is_filtered()`.
31 * @method string[] get_filtered_taxonomies_query_vars() Returns the query vars of all filtered taxonomies. See `Model\Taxonomies::get_filtered_query_vars()`.
32 */
33 class PLL_Model {
34 /**
35 * Internal non persistent cache object.
36 *
37 * @var PLL_Cache<mixed>
38 */
39 public $cache;
40
41 /**
42 * Stores the plugin options.
43 *
44 * @var Options
45 */
46 public $options;
47
48 /**
49 * Translatable objects registry.
50 *
51 * @since 3.4
52 *
53 * @var PLL_Translatable_Objects
54 */
55 public $translatable_objects;
56
57 /**
58 * Translated post model.
59 *
60 * @var PLL_Translated_Post
61 */
62 public $post;
63
64 /**
65 * Translated term model.
66 *
67 * @var PLL_Translated_Term
68 */
69 public $term;
70
71 /**
72 * Model for the languages.
73 *
74 * @var \WP_Syntex\Polylang\Model\Languages
75 */
76 public $languages;
77
78 /**
79 * Model for taxonomies translated by Polylang.
80 *
81 * @var \WP_Syntex\Polylang\Model\Post_Types
82 */
83 public $post_types;
84
85 /**
86 * Model for taxonomies filtered/translated by Polylang.
87 *
88 * @var \WP_Syntex\Polylang\Model\Taxonomies
89 */
90 public $taxonomies;
91
92 /**
93 * Constructor.
94 * Setups translated objects sub models.
95 * Setups filters and actions.
96 *
97 * @since 1.2
98 * @since 3.7 Type of parameter `$options` changed from `array` to `Options`.
99 *
100 * @param Options $options Polylang options.
101 */
102 public function __construct( Options $options ) {
103 $this->options = $options;
104 $this->cache = new PLL_Cache();
105 $this->translatable_objects = new PLL_Translatable_Objects();
106 $this->languages = new Model\Languages( $this->options, $this->translatable_objects, $this->cache );
107
108 $this->languages->register_proxy( new Model\Hide_Empty() );
109 $this->languages->register_proxy( new Model\Hide_Default() );
110
111 $this->post = $this->translatable_objects->register( new PLL_Translated_Post( $this ) ); // Translated post sub model.
112 $this->term = $this->translatable_objects->register( new PLL_Translated_Term( $this ) ); // Translated term sub model.
113
114 $this->post_types = new Model\Post_Types( $this->post );
115 $this->taxonomies = new Model\Taxonomies( $this->term );
116
117 // We need to clean languages cache when editing a language and when modifying the permalink structure.
118 add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
119 add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
120 add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
121 add_action( 'update_option_home', array( $this, 'clean_languages_cache' ) );
122
123 add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
124
125 // Just in case someone would like to display the language description ;).
126 add_filter( 'language_description', '__return_empty_string' );
127
128 add_action( 'delete_transient_' . Model\Languages::TRANSIENT_NAME, array( $this->languages, 'delete_transient_from_options_table' ) );
129 }
130
131 /**
132 * Backward compatibility for methods that have been moved to sub-models.
133 *
134 * @since 3.7
135 *
136 * @throws BadMethodCallException If the method is not found.
137 *
138 * @param string $name Name of the method being called.
139 * @param array $arguments Enumerated array containing the parameters passed to the $name'ed method.
140 * @return mixed
141 */
142 public function __call( string $name, array $arguments ) {
143 $methods = array(
144 // Languages.
145 'has_languages' => array( $this->languages, 'has' ),
146 'get_languages_list' => array( $this->languages, 'get_list' ),
147 'are_languages_ready' => array( $this->languages, 'are_ready' ),
148 'set_languages_ready' => array( $this->languages, 'set_ready' ),
149 'get_language' => array( $this->languages, 'get' ),
150 'add_language' => array( $this->languages, 'add' ),
151 'delete_language' => array( $this->languages, 'delete' ),
152 'update_language' => array( $this->languages, 'update' ),
153 'get_default_language' => array( $this->languages, 'get_default' ),
154 'update_default_lang' => array( $this->languages, 'update_default' ),
155 'maybe_create_language_terms' => array( $this->languages, 'maybe_create_terms' ),
156 // Post types.
157 'get_translated_post_types' => array( $this->post_types, 'get_translated' ),
158 'is_translated_post_type' => array( $this->post_types, 'is_translated' ),
159 // Taxonomies.
160 'get_translated_taxonomies' => array( $this->taxonomies, 'get_translated' ),
161 'is_translated_taxonomy' => array( $this->taxonomies, 'is_translated' ),
162 'get_filtered_taxonomies' => array( $this->taxonomies, 'get_filtered' ),
163 'is_filtered_taxonomy' => array( $this->taxonomies, 'is_filtered' ),
164 'get_filtered_taxonomies_query_vars' => array( $this->taxonomies, 'get_filtered_query_vars' ),
165 );
166
167 if ( isset( $methods[ $name ] ) ) {
168 return call_user_func_array( $methods[ $name ], $arguments );
169 }
170
171 $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
172 throw new BadMethodCallException(
173 sprintf(
174 'Call to undefined method PLL()->model->%1$s() in %2$s on line %3$s' . "\nError handler",
175 esc_html( $name ),
176 esc_html( $debug[0]['file'] ?? '' ),
177 absint( $debug[0]['line'] ?? 0 )
178 )
179 );
180 }
181
182 /**
183 * Cleans language cache
184 * can be called directly with no parameter
185 * called by the 'edited_term_taxonomy' filter with 2 parameters when count needs to be updated
186 *
187 * @since 1.2
188 *
189 * @param int $term not used
190 * @param string $taxonomy taxonomy name
191 * @return void
192 */
193 public function clean_languages_cache( $term = 0, $taxonomy = null ): void {
194 if ( empty( $taxonomy ) || 'language' === $taxonomy ) {
195 $this->languages->clean_cache();
196 }
197 }
198
199 /**
200 * Don't query term metas when only our taxonomies are queried
201 *
202 * @since 2.3
203 *
204 * @param array $args WP_Term_Query arguments
205 * @return array
206 */
207 public function get_terms_args( $args ) {
208 $taxonomies = $this->translatable_objects->get_taxonomy_names();
209
210 if ( isset( $args['taxonomy'] ) && ! array_diff( (array) $args['taxonomy'], $taxonomies ) ) {
211 $args['update_term_meta_cache'] = false;
212 }
213 return $args;
214 }
215
216 /**
217 * Adds terms clauses to the term query to filter them by languages.
218 *
219 * @since 1.2
220 * @since 3.8 Accepts an object or an array of objects for the second param.
221 *
222 * @param string[] $clauses The list of sql clauses in terms query.
223 * @param PLL_Language[]|PLL_Language $languages `PLL_Language` object or array of `PLL_Language` objects.
224 * @return string[] Modified list of clauses.
225 */
226 public function terms_clauses( $clauses, $languages ) {
227 if ( ! empty( $languages ) && false === strpos( $clauses['join'], 'pll_tr' ) ) {
228 $clauses['join'] .= $this->term->join_clause();
229 $clauses['where'] .= $this->term->where_clause( $languages );
230 }
231 return $clauses;
232 }
233
234 /**
235 * It is possible to have several terms with the same name in the same taxonomy ( one per language )
236 * but the native term_exists() will return true even if only one exists.
237 * So here the function adds the language parameter.
238 *
239 * @since 1.4
240 *
241 * @param string $term_name The term name.
242 * @param string $taxonomy Taxonomy name.
243 * @param int $parent Parent term id.
244 * @param string|PLL_Language $language The language slug or object.
245 * @return int The `term_id` of the found term. 0 otherwise.
246 *
247 * @phpstan-return int<0, max>
248 */
249 public function term_exists( $term_name, $taxonomy, $parent, $language ): int {
250 global $wpdb;
251
252 $language = $this->languages->get( $language );
253 if ( empty( $language ) ) {
254 return 0;
255 }
256
257 $term_name = trim( wp_unslash( $term_name ) );
258 $term_name = _wp_specialchars( $term_name );
259
260 $select = "SELECT t.term_id FROM $wpdb->terms AS t";
261 $join = " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
262 $join .= $this->term->join_clause();
263 $where = $wpdb->prepare( ' WHERE tt.taxonomy = %s AND t.name = %s', $taxonomy, $term_name );
264 $where .= $this->term->where_clause( $language );
265
266 if ( $parent > 0 ) {
267 $where .= $wpdb->prepare( ' AND tt.parent = %d', $parent );
268 }
269
270 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
271 $term_id = $wpdb->get_var( $select . $join . $where );
272 return max( 0, (int) $term_id );
273 }
274
275 /**
276 * Checks if a term slug exists in a given language, taxonomy, hierarchy.
277 *
278 * @since 1.9
279 * @since 2.8 Moved from PLL_Share_Term_Slug::term_exists() to PLL_Model::term_exists_by_slug().
280 * @since 3.8 Refactored to use `get_terms()` instead of direct SQL query.
281 *
282 * @param string $slug The term slug to test.
283 * @param string|PLL_Language $language The language slug or object.
284 * @param string $taxonomy Optional taxonomy name.
285 * @param int $parent Optional parent term id.
286 * @return int The `term_id` of the found term. 0 otherwise.
287 */
288 public function term_exists_by_slug( $slug, $language, $taxonomy = '', $parent = 0 ): int {
289 $language = $this->languages->get( $language );
290 if ( empty( $language ) ) {
291 return 0;
292 }
293
294 $args = array(
295 'slug' => $slug,
296 'hide_empty' => false,
297 'fields' => 'ids',
298 'lang' => $language->slug,
299 'update_term_meta_cache' => false,
300 );
301
302 if ( ! empty( $taxonomy ) ) {
303 $args['taxonomy'] = $taxonomy;
304 }
305
306 if ( $parent > 0 ) {
307 $args['parent'] = $parent;
308 }
309
310 $terms = get_terms( $args );
311
312 return ! empty( $terms ) && is_array( $terms ) ? (int) reset( $terms ) : 0;
313 }
314
315 /**
316 * Returns the number of posts per language in a date, author or post type archive.
317 *
318 * @since 1.2
319 *
320 * @param PLL_Language $lang PLL_Language instance.
321 * @param array $q {
322 * WP_Query arguments:
323 *
324 * @type string|string[] $post_type Post type or array of post types, defaults to 'post'.
325 * @type int $m Combination YearMonth. Accepts any four-digit year and month.
326 * @type int $year Four-digit year.
327 * @type int $monthnum Two-digit month.
328 * @type int $day Day of the month.
329 * @type int $author Author id.
330 * @type string $author_name User 'user_nicename'.
331 * @type string $post_format Post format.
332 * @type string $post_status Post status, defaults to 'publish'.
333 * }
334 * @return int
335 *
336 * @phpstan-param array{
337 * post_type?: non-falsy-string|array<non-falsy-string>,
338 * post_status?: non-falsy-string,
339 * m?: numeric-string,
340 * year?: positive-int,
341 * monthnum?: int<1, 12>,
342 * day?: int<1, 31>,
343 * author?: int<1, max>,
344 * author_name?: non-falsy-string,
345 * post_format?: non-falsy-string
346 * } $q
347 */
348 public function count_posts( $lang, $q = array() ): int {
349 global $wpdb;
350
351 $q = array_merge( array( 'post_type' => 'post', 'post_status' => 'publish' ), $q );
352
353 if ( ! is_array( $q['post_type'] ) ) {
354 $q['post_type'] = array( $q['post_type'] );
355 }
356
357 foreach ( $q['post_type'] as $key => $type ) {
358 if ( ! post_type_exists( $type ) ) {
359 unset( $q['post_type'][ $key ] );
360 }
361 }
362
363 if ( empty( $q['post_type'] ) ) {
364 $q['post_type'] = array( 'post' ); // We *need* a post type.
365 }
366
367 $cache_key = $this->cache->get_unique_key( 'pll_count_posts_', $q );
368 $counts = $this->get_counts_cache( $cache_key );
369
370 if ( ! is_array( $counts ) ) {
371 $counts = array();
372 $select = "SELECT pll_tr.term_taxonomy_id, post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
373 $join = $this->post->join_clause();
374 $where = sprintf( " WHERE {$wpdb->posts}.post_type IN ( '%s' )", implode( "', '", esc_sql( $q['post_type'] ) ) );
375 $where .= $this->post->where_clause( $this->languages->get_list() );
376 $where .= " AND post_status != 'auto-draft'";
377 $groupby = ' GROUP BY pll_tr.term_taxonomy_id, post_status';
378
379 if ( ! empty( $q['m'] ) ) {
380 $q['m'] = '' . preg_replace( '|[^0-9]|', '', $q['m'] );
381 $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 0, 4 ) );
382 if ( strlen( $q['m'] ) > 5 ) {
383 $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 4, 2 ) );
384 }
385 if ( strlen( $q['m'] ) > 7 ) {
386 $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 6, 2 ) );
387 }
388 }
389
390 if ( ! empty( $q['year'] ) ) {
391 $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", $q['year'] );
392 }
393
394 if ( ! empty( $q['monthnum'] ) ) {
395 $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", $q['monthnum'] );
396 }
397
398 if ( ! empty( $q['day'] ) ) {
399 $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", $q['day'] );
400 }
401
402 if ( ! empty( $q['author_name'] ) ) {
403 $author = get_user_by( 'slug', sanitize_title_for_query( $q['author_name'] ) );
404 if ( $author ) {
405 $q['author'] = $author->ID;
406 }
407 }
408
409 if ( ! empty( $q['author'] ) ) {
410 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $q['author'] );
411 }
412
413 // Filtered taxonomies ( post_format ).
414 foreach ( $this->taxonomies->get_filtered_query_vars() as $tax_qv ) {
415
416 if ( ! empty( $q[ $tax_qv ] ) ) {
417 $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.object_id = {$wpdb->posts}.ID";
418 $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
419 $join .= " INNER JOIN {$wpdb->terms} AS t ON t.term_id = tt.term_id";
420 $where .= $wpdb->prepare( ' AND t.slug = %s', $q[ $tax_qv ] );
421 }
422 }
423
424 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
425 $res = $wpdb->get_results( $select . $join . $where . $groupby, ARRAY_A );
426
427 foreach ( (array) $res as $row ) {
428 $counts[ $row['term_taxonomy_id'] ][ $row['post_status'] ] = $row['num_posts'];
429 }
430
431 $this->set_counts_cache( $cache_key, $counts );
432 }
433
434 $term_taxonomy_id = $lang->get_tax_prop( 'language', 'term_taxonomy_id' );
435
436 if ( 'any' === $q['post_status'] ) {
437 return isset( $counts[ $term_taxonomy_id ] ) ? (int) array_sum( $counts[ $term_taxonomy_id ] ) : 0;
438 }
439
440 return $counts[ $term_taxonomy_id ][ $q['post_status'] ] ?? 0;
441 }
442
443 /**
444 * Setup the links model based on options.
445 *
446 * @since 1.2
447 *
448 * @return PLL_Links_Model
449 */
450 public function get_links_model(): PLL_Links_Model {
451 $c = array( 'Directory', 'Directory', 'Subdomain', 'Domain' );
452 $class = get_option( 'permalink_structure' ) ? 'PLL_Links_' . $c[ $this->options['force_lang'] ] : 'PLL_Links_Default';
453
454 /**
455 * Filters the links model class to use.
456 * /!\ this filter is fired *before* the $polylang object is available.
457 *
458 * @since 2.1.1
459 *
460 * @param string $class A class name: PLL_Links_Default, PLL_Links_Directory, PLL_Links_Subdomain, PLL_Links_Domain.
461 */
462 $class = apply_filters( 'pll_links_model', $class );
463
464 return new $class( $this );
465 }
466
467 /**
468 * Returns a list of object IDs without language (used in settings and wizard).
469 *
470 * @since 0.9
471 * @since 2.2.6 Added the `$limit` parameter.
472 * @since 3.4 Added the `$types` parameter.
473 *
474 * @param int $limit Optional. Max number of IDs to return. Defaults to -1 (no limit).
475 * @param string[] $types Optional. Types to handle (@see PLL_Translatable_Object::get_type()). Defaults to
476 * an empty array (all types).
477 * @return int[][]|false {
478 * IDs of objects without language.
479 *
480 * @type int[] $posts Array of post ids.
481 * @type int[] $terms Array of term ids.
482 * }
483 *
484 * @phpstan-param -1|positive-int $limit
485 */
486 public function get_objects_with_no_lang( $limit = -1, array $types = array() ) {
487 /**
488 * Filters the max number of IDs to return when searching objects with no language.
489 * This filter can be used to decrease the memory usage in case the number of objects
490 * without language is too big. Using a negative value is equivalent to have no limit.
491 *
492 * @since 2.2.6
493 * @since 3.4 Added the `$types` parameter.
494 *
495 * @param int $limit Max number of IDs to retrieve from the database.
496 * @param string[] $types Types to handle (@see PLL_Translatable_Object::get_type()). An empty array means all
497 * types.
498 */
499 $limit = apply_filters( 'get_objects_with_no_lang_limit', $limit, $types );
500 $limit = $limit < 1 ? -1 : max( (int) $limit, 1 );
501 $objects = array();
502
503 foreach ( $this->translatable_objects as $type => $object ) {
504 if ( ! empty( $types ) && ! in_array( $type, $types, true ) ) {
505 continue;
506 }
507
508 $ids = $object->get_objects_with_no_lang( $limit );
509
510 if ( empty( $ids ) ) {
511 continue;
512 }
513
514 // The trailing 's' in the array key is for backward compatibility.
515 $objects[ "{$type}s" ] = $ids;
516 }
517
518 $objects = ! empty( $objects ) ? $objects : false;
519
520 /**
521 * Filters the list of IDs of untranslated objects.
522 *
523 * @since 0.9
524 * @since 3.4 Added the `$limit` and `$types` parameters.
525 *
526 * @param int[][]|false $objects List of lists of object IDs, `false` if no IDs found.
527 * @param int $limit Max number of IDs to retrieve from the database.
528 * @param string[] $types Types to handle (@see PLL_Translatable_Object::get_type()). An empty array
529 * means all types.
530 */
531 return apply_filters( 'pll_get_objects_with_no_lang', $objects, $limit, $types );
532 }
533
534 /**
535 * Returns ids of post without language.
536 *
537 * @since 3.1
538 *
539 * @param string|string[] $post_types A translated post type or an array of translated post types.
540 * @param int $limit Max number of objects to return. `-1` to return all of them.
541 * @return int[]
542 *
543 * @phpstan-param -1|positive-int $limit
544 * @phpstan-return list<positive-int>
545 */
546 public function get_posts_with_no_lang( $post_types, $limit ): array {
547 return $this->translatable_objects->get( 'post' )->get_objects_with_no_lang( $limit, (array) $post_types );
548 }
549
550 /**
551 * Returns ids of terms without language.
552 *
553 * @since 3.1
554 *
555 * @param string|string[] $taxonomies A translated taxonomy or an array of taxonomies post types.
556 * @param int $limit Max number of objects to return. `-1` to return all of them.
557 * @return int[]
558 *
559 * @phpstan-param -1|positive-int $limit
560 * @phpstan-return list<positive-int>
561 */
562 public function get_terms_with_no_lang( $taxonomies, $limit ): array {
563 return $this->translatable_objects->get( 'term' )->get_objects_with_no_lang( $limit, (array) $taxonomies );
564 }
565
566 /**
567 * Assigns the default language to objects in mass.
568 *
569 * @since 1.2
570 * @since 3.4 Moved from PLL_Admin_Model class.
571 * Removed `$limit` parameter, added `$lang` and `$types` parameters.
572 *
573 * @param PLL_Language|null $lang Optional. The language to assign to objects. Defaults to `null` (default language).
574 * @param string[] $types Optional. Types to handle (@see PLL_Translatable_Object::get_type()). Defaults
575 * to an empty array (all types).
576 * @return void
577 */
578 public function set_language_in_mass( $lang = null, array $types = array() ): void {
579 if ( ! $lang instanceof PLL_Language ) {
580 $lang = $this->languages->get_default();
581
582 if ( empty( $lang ) ) {
583 return;
584 }
585 }
586
587 // 1000 is an arbitrary value that will be filtered by `get_objects_with_no_lang_limit`.
588 $nolang = $this->get_objects_with_no_lang( 1000, $types );
589
590 if ( empty( $nolang ) ) {
591 return;
592 }
593
594 /**
595 * Keep track of types where we set the language:
596 * those are types where we may have more items to process if we have more than 1000 items in total.
597 * This will prevent unnecessary SQL queries in the next recursion: if we have 0 items in this recursion for
598 * a type, we'll still have 0 in the next one, no need for a new query.
599 */
600 $types_with_objects = array();
601
602 foreach ( $this->translatable_objects as $type => $object ) {
603 if ( empty( $nolang[ "{$type}s" ] ) ) {
604 continue;
605 }
606
607 if ( ! empty( $types ) && ! in_array( $type, $types, true ) ) {
608 continue;
609 }
610
611 $object->set_language_in_mass( $nolang[ "{$type}s" ], $lang );
612 $types_with_objects[] = $type;
613 }
614
615 if ( empty( $types_with_objects ) ) {
616 return;
617 }
618
619 $this->set_language_in_mass( $lang, $types_with_objects );
620 }
621
622 /**
623 * Returns the number of posts grouped per language from the cache.
624 *
625 * @since 3.9
626 *
627 * @param string $cache_key The cache key.
628 * @return array|false The counts grouped per language, false if the cache is empty.
629 */
630 private function get_counts_cache( string $cache_key ) {
631 $last_changed = wp_cache_get_last_changed( 'posts' );
632
633 if ( ! function_exists( 'wp_cache_get_salted' ) ) {
634 // Backward compatibility with WordPress < 6.9.
635 $cache_key = "{$cache_key}:{$last_changed}";
636 $counts = wp_cache_get( $cache_key, 'posts' );
637 } else {
638 $counts = wp_cache_get_salted( $cache_key, 'posts', $last_changed );
639 }
640
641 return is_array( $counts ) ? $counts : false;
642 }
643
644 /**
645 * Stores the number of posts grouped per language in the cache.
646 *
647 * @since 3.9
648 *
649 * @param string $cache_key The cache key.
650 * @param int[][] $counts The number of posts grouped per language and post status.
651 * @return bool True if the value has been stored, false otherwise.
652 */
653 private function set_counts_cache( $cache_key, array $counts ): bool {
654 $last_changed = wp_cache_get_last_changed( 'posts' );
655
656 if ( ! function_exists( 'wp_cache_set_salted' ) ) {
657 // Backward compatibility with WordPress < 6.9.
658 $cache_key = "{$cache_key}:{$last_changed}";
659 return wp_cache_set( $cache_key, $counts, 'posts' );
660 }
661
662 return wp_cache_set_salted( $cache_key, $counts, 'posts', $last_changed );
663 }
664 }
665