PluginProbe
Polylang / 2.8.4
Polylang v2.8.4
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 / model.php

model.php in Polylang 2.8.4, at include/model.php

674 lines 23.5 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 * Setups the language and translations model based on WordPress taxonomies
8 *
9 * @since 1.2
10 */
11 class PLL_Model {
12 public $cache; // Our internal non persistent cache object
13 public $options;
14 public $post, $term; // Translated objects models
15
16 /**
17 * Constructor
18 * setups translated objects sub models
19 * setups filters and actions
20 *
21 * @since 1.2
22 *
23 * @param array $options Polylang options
24 */
25 public function __construct( &$options ) {
26 $this->options = &$options;
27
28 $this->cache = new PLL_Cache();
29 $this->post = new PLL_Translated_Post( $this ); // translated post sub model
30 $this->term = new PLL_Translated_Term( $this ); // translated term sub model
31
32 // We need to clean languages cache when editing a language and when modifying the permalink structure
33 add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
34 add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
35 add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
36 add_action( 'update_option_home', array( $this, 'clean_languages_cache' ) );
37
38 add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
39
40 // Just in case someone would like to display the language description ;- )
41 add_filter( 'language_description', '__return_empty_string' );
42 }
43
44 /**
45 * Returns the list of available languages
46 * caches the list in a db transient ( except flags ), unless PLL_CACHE_LANGUAGES is set to false
47 * caches the list ( with flags ) in the private property $languages
48 *
49 * List of parameters accepted in $args:
50 *
51 * hide_empty => hides languages with no posts if set to true ( defaults to false )
52 * fields => return only that field if set ( see PLL_Language for a list of fields )
53 *
54 * @since 0.1
55 *
56 * @param array $args
57 * @return array|string|int list of PLL_Language objects or PLL_Language object properties
58 */
59 public function get_languages_list( $args = array() ) {
60 if ( false === $languages = $this->cache->get( 'languages' ) ) {
61
62 // Create the languages from taxonomies
63 if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || false === ( $languages = get_transient( 'pll_languages_list' ) ) ) {
64 $languages = get_terms( 'language', array( 'hide_empty' => false, 'orderby' => 'term_group' ) );
65 $languages = empty( $languages ) || is_wp_error( $languages ) ? array() : $languages;
66
67 $term_languages = get_terms( 'term_language', array( 'hide_empty' => false ) );
68 $term_languages = empty( $term_languages ) || is_wp_error( $term_languages ) ?
69 array() : array_combine( wp_list_pluck( $term_languages, 'slug' ), $term_languages );
70
71 if ( ! empty( $languages ) && ! empty( $term_languages ) ) {
72 // Don't use array_map + create_function to instantiate an autoloaded class as it breaks badly in old versions of PHP
73 foreach ( $languages as $k => $v ) {
74 $languages[ $k ] = new PLL_Language( $v, $term_languages[ 'pll_' . $v->slug ] );
75 }
76
77 // We will need the languages list to allow its access in the filter below
78 $this->cache->set( 'languages', $languages );
79
80 /**
81 * Filter the list of languages *before* it is stored in the persistent cache
82 * /!\ this filter is fired *before* the $polylang object is available
83 *
84 * @since 1.7.5
85 *
86 * @param array $languages the list of language objects
87 * @param object $model PLL_Model object
88 */
89 $languages = apply_filters( 'pll_languages_list', $languages, $this );
90
91 // Don't store directly objects as it badly break with some hosts ( GoDaddy ) due to race conditions when using object cache
92 // Thanks to captin411 for catching this!
93 // See https://wordpress.org/support/topic/fatal-error-pll_model_languages_list?replies=8#post-6782255;
94 set_transient( 'pll_languages_list', array_map( 'get_object_vars', $languages ) );
95 }
96 else {
97 $languages = array(); // In case something went wrong
98 }
99 }
100
101 // Create the languages directly from arrays stored in transients
102 else {
103 foreach ( $languages as $k => $v ) {
104 $languages[ $k ] = new PLL_Language( $v );
105 }
106 }
107
108 /**
109 * Filter the list of languages *after* it is stored in the persistent cache
110 * /!\ this filter is fired *before* the $polylang object is available
111 *
112 * @since 1.8
113 *
114 * @param array $languages the list of language objects
115 */
116 $languages = apply_filters( 'pll_after_languages_cache', $languages );
117 $this->cache->set( 'languages', $languages );
118 }
119
120 $args = wp_parse_args( $args, array( 'hide_empty' => false ) );
121
122 // Remove empty languages if requested
123 if ( $args['hide_empty'] ) {
124 $languages = wp_list_filter( $languages, array( 'count' => 0 ), 'NOT' );
125 }
126
127 return empty( $args['fields'] ) ? $languages : wp_list_pluck( $languages, $args['fields'] );
128 }
129
130 /**
131 * Cleans language cache
132 * can be called directly with no parameter
133 * called by the 'edited_term_taxonomy' filter with 2 parameters when count needs to be updated
134 *
135 * @since 1.2
136 *
137 * @param int $term not used
138 * @param string $taxonomy taxonomy name
139 */
140 public function clean_languages_cache( $term = 0, $taxonomy = null ) {
141 if ( empty( $taxonomy ) || 'language' == $taxonomy ) {
142 delete_transient( 'pll_languages_list' );
143 $this->cache->clean();
144 }
145 }
146
147 /**
148 * Don't query term metas when only our taxonomies are queried
149 *
150 * @since 2.3
151 *
152 * @param array $args WP_Term_Query arguments
153 * @return array
154 */
155 public function get_terms_args( $args ) {
156 if ( isset( $args['taxonomy'] ) && ! array_diff( (array) $args['taxonomy'], array( 'language', 'term_language', 'post_translations', 'term_translations' ) ) ) {
157 $args['update_term_meta_cache'] = false;
158 }
159 return $args;
160 }
161
162 /**
163 * Returns the language by its term_id, tl_term_id, slug or locale
164 *
165 * @since 0.1
166 *
167 * @param int|string $value term_id, tl_term_id, slug or locale of the queried language
168 * @return object|bool PLL_Language object, false if no language found
169 */
170 public function get_language( $value ) {
171 if ( is_object( $value ) ) {
172 return $value instanceof PLL_Language ? $value : $this->get_language( $value->term_id ); // will force cast to PLL_Language
173 }
174
175 if ( false === $return = $this->cache->get( 'language:' . $value ) ) {
176 foreach ( $this->get_languages_list() as $lang ) {
177 $this->cache->set( 'language:' . $lang->term_id, $lang );
178 $this->cache->set( 'language:' . $lang->tl_term_id, $lang );
179 $this->cache->set( 'language:' . $lang->slug, $lang );
180 $this->cache->set( 'language:' . $lang->locale, $lang );
181 $this->cache->set( 'language:' . $lang->w3c, $lang );
182 }
183 $return = $this->cache->get( 'language:' . $value );
184 }
185
186 return $return;
187 }
188
189 /**
190 * Adds terms clauses to get_terms to filter them by languages - used in both frontend and admin
191 *
192 * @since 1.2
193 *
194 * @param array $clauses the list of sql clauses in terms query
195 * @param object $lang PLL_Language object
196 * @return array modified list of clauses
197 */
198 public function terms_clauses( $clauses, $lang ) {
199 if ( ! empty( $lang ) && false === strpos( $clauses['join'], 'pll_tr' ) ) {
200 $clauses['join'] .= $this->term->join_clause();
201 $clauses['where'] .= $this->term->where_clause( $lang );
202 }
203 return $clauses;
204 }
205
206 /**
207 * Returns post types that need to be translated
208 * the post types list is cached for better better performance
209 * wait for 'after_setup_theme' to apply the cache to allow themes adding the filter in functions.php
210 *
211 * @since 1.2
212 *
213 * @param bool $filter true if we should return only valid registered post types
214 * @return array post type names for which Polylang manages languages and translations
215 */
216 public function get_translated_post_types( $filter = true ) {
217 if ( false === $post_types = $this->cache->get( 'post_types' ) ) {
218 $post_types = array( 'post' => 'post', 'page' => 'page', 'wp_block' => 'wp_block' );
219
220 if ( ! empty( $this->options['media_support'] ) ) {
221 $post_types['attachment'] = 'attachment';
222 }
223
224 if ( ! empty( $this->options['post_types'] ) && is_array( $this->options['post_types'] ) ) {
225 $post_types = array_merge( $post_types, array_combine( $this->options['post_types'], $this->options['post_types'] ) );
226 }
227
228 /**
229 * Filter the list of post types available for translation.
230 * The default are post types which have the parameter ‘public’ set to true.
231 * The filter must be added soon in the WordPress loading process:
232 * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
233 *
234 * @since 0.8
235 *
236 * @param array $post_types list of post type names
237 * @param bool $is_settings true when displaying the list of custom post types in Polylang settings
238 */
239 $post_types = apply_filters( 'pll_get_post_types', $post_types, false );
240
241 if ( did_action( 'after_setup_theme' ) ) {
242 $this->cache->set( 'post_types', $post_types );
243 }
244 }
245
246 return $filter ? array_intersect( $post_types, get_post_types() ) : $post_types;
247 }
248
249 /**
250 * Returns true if Polylang manages languages and translations for this post type
251 *
252 * @since 1.2
253 *
254 * @param string|array $post_type post type name or array of post type names
255 * @return bool
256 */
257 public function is_translated_post_type( $post_type ) {
258 $post_types = $this->get_translated_post_types( false );
259 return ( is_array( $post_type ) && array_intersect( $post_type, $post_types ) || in_array( $post_type, $post_types ) || 'any' === $post_type && ! empty( $post_types ) );
260 }
261
262 /**
263 * Return taxonomies that need to be translated
264 *
265 * @since 1.2
266 *
267 * @param bool $filter true if we should return only valid registered taxonomies
268 * @return array array of registered taxonomy names for which Polylang manages languages and translations
269 */
270 public function get_translated_taxonomies( $filter = true ) {
271 if ( false === $taxonomies = $this->cache->get( 'taxonomies' ) ) {
272 $taxonomies = array( 'category' => 'category', 'post_tag' => 'post_tag' );
273
274 if ( ! empty( $this->options['taxonomies'] ) && is_array( $this->options['taxonomies'] ) ) {
275 $taxonomies = array_merge( $taxonomies, array_combine( $this->options['taxonomies'], $this->options['taxonomies'] ) );
276 }
277
278 /**
279 * Filter the list of taxonomies available for translation.
280 * The default are taxonomies which have the parameter ‘public’ set to true.
281 * The filter must be added soon in the WordPress loading process:
282 * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
283 *
284 * @since 0.8
285 *
286 * @param array $taxonomies list of taxonomy names
287 * @param bool $is_settings true when displaying the list of custom taxonomies in Polylang settings
288 */
289 $taxonomies = apply_filters( 'pll_get_taxonomies', $taxonomies, false );
290 if ( did_action( 'after_setup_theme' ) ) {
291 $this->cache->set( 'taxonomies', $taxonomies );
292 }
293 }
294
295 return $filter ? array_intersect( $taxonomies, get_taxonomies() ) : $taxonomies;
296 }
297
298 /**
299 * Returns true if Polylang manages languages and translations for this taxonomy
300 *
301 * @since 1.2
302 *
303 * @param string|array $tax taxonomy name or array of taxonomy names
304 * @return bool
305 */
306 public function is_translated_taxonomy( $tax ) {
307 $taxonomies = $this->get_translated_taxonomies( false );
308 return ( is_array( $tax ) && array_intersect( $tax, $taxonomies ) || in_array( $tax, $taxonomies ) );
309 }
310
311 /**
312 * Return taxonomies that need to be filtered ( post_format like )
313 *
314 * @since 1.7
315 *
316 * @param bool $filter true if we should return only valid registered taxonomies
317 * @return array array of registered taxonomy names
318 */
319 public function get_filtered_taxonomies( $filter = true ) {
320 if ( did_action( 'after_setup_theme' ) ) {
321 static $taxonomies = null;
322 }
323
324 if ( empty( $taxonomies ) ) {
325 $taxonomies = array( 'post_format' => 'post_format' );
326
327 /**
328 * Filter the list of taxonomies not translatable but filtered by language.
329 * Includes only the post format by default
330 * The filter must be added soon in the WordPress loading process:
331 * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
332 *
333 * @since 1.7
334 *
335 * @param array $taxonomies list of taxonomy names
336 * @param bool $is_settings true when displaying the list of custom taxonomies in Polylang settings
337 */
338 $taxonomies = apply_filters( 'pll_filtered_taxonomies', $taxonomies, false );
339 }
340
341 return $filter ? array_intersect( $taxonomies, get_taxonomies() ) : $taxonomies;
342 }
343
344 /**
345 * Returns true if Polylang filters this taxonomy per language
346 *
347 * @since 1.7
348 *
349 * @param string|array $tax taxonomy name or array of taxonomy names
350 * @return bool
351 */
352 public function is_filtered_taxonomy( $tax ) {
353 $taxonomies = $this->get_filtered_taxonomies( false );
354 return ( is_array( $tax ) && array_intersect( $tax, $taxonomies ) || in_array( $tax, $taxonomies ) );
355 }
356
357 /**
358 * Returns the query vars of all filtered taxonomies
359 *
360 * @since 1.7
361 *
362 * @return array
363 */
364 public function get_filtered_taxonomies_query_vars() {
365 $query_vars = array();
366 foreach ( $this->get_filtered_taxonomies() as $filtered_tax ) {
367 $tax = get_taxonomy( $filtered_tax );
368 $query_vars[] = $tax->query_var;
369 }
370 return $query_vars;
371 }
372
373 /**
374 * Create a default category for a language
375 *
376 * @since 1.2
377 *
378 * @param object|string|int $lang language
379 */
380 public function create_default_category( $lang ) {
381 $lang = $this->get_language( $lang );
382
383 // create a new category
384 // FIXME this is translated in admin language when we would like it in $lang
385 $cat_name = __( 'Uncategorized', 'polylang' );
386 $cat_slug = sanitize_title( $cat_name . '-' . $lang->slug );
387 $cat = wp_insert_term( $cat_name, 'category', array( 'slug' => $cat_slug ) );
388
389 // check that the category was not previously created ( in case the language was deleted and recreated )
390 $cat = isset( $cat->error_data['term_exists'] ) ? $cat->error_data['term_exists'] : $cat['term_id'];
391
392 // set language
393 $this->term->set_language( (int) $cat, $lang );
394
395 // this is a translation of the default category
396 $default = (int) get_option( 'default_category' );
397 $translations = $this->term->get_translations( $default );
398 if ( empty( $translations ) ) {
399 if ( $lg = $this->term->get_language( $default ) ) {
400 $translations[ $lg->slug ] = $default;
401 }
402 else {
403 $translations = array();
404 }
405 }
406
407 $this->term->save_translations( (int) $cat, $translations );
408 }
409
410 /**
411 * It is possible to have several terms with the same name in the same taxonomy ( one per language )
412 * but the native term_exists will return true even if only one exists
413 * so here the function adds the language parameter
414 *
415 * @since 1.4
416 *
417 * @param string $term_name the term name
418 * @param string $taxonomy taxonomy name
419 * @param int $parent parent term id
420 * @param string|object $language the language slug or object
421 * @return null|int the term_id of the found term
422 */
423 public function term_exists( $term_name, $taxonomy, $parent, $language ) {
424 global $wpdb;
425
426 $term_name = trim( wp_unslash( $term_name ) );
427 $term_name = _wp_specialchars( $term_name );
428
429 $select = "SELECT t.term_id FROM $wpdb->terms AS t";
430 $join = " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
431 $join .= $this->term->join_clause();
432 $where = $wpdb->prepare( ' WHERE tt.taxonomy = %s AND t.name = %s', $taxonomy, $term_name );
433 $where .= $this->term->where_clause( $this->get_language( $language ) );
434
435 if ( $parent > 0 ) {
436 $where .= $wpdb->prepare( ' AND tt.parent = %d', $parent );
437 }
438
439 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
440 return $wpdb->get_var( $select . $join . $where );
441 }
442
443 /**
444 * Checks if a term slug exists in a given language, taxonomy, hierarchy
445 *
446 * @since 1.9
447 * @since 2.8 Moved from PLL_Share_Term_Slug::term_exists() to PLL_Model::term_exists_by_slug()
448 *
449 * @param string $slug The term slug to test.
450 * @param string|object $language The language slug or object.
451 * @param string $taxonomy Optional taxonomy name.
452 * @param int $parent Optional parent term id.
453 * @return null|int The term_id of the found term.
454 */
455 public function term_exists_by_slug( $slug, $language, $taxonomy = '', $parent = 0 ) {
456 global $wpdb;
457
458 $select = "SELECT t.term_id FROM {$wpdb->terms} AS t";
459 $join = " INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
460 $join .= $this->term->join_clause();
461 $where = $wpdb->prepare( ' WHERE t.slug = %s', $slug );
462 $where .= $this->term->where_clause( $this->get_language( $language ) );
463
464 if ( ! empty( $taxonomy ) ) {
465 $where .= $wpdb->prepare( ' AND tt.taxonomy = %s', $taxonomy );
466 }
467
468 if ( $parent > 0 ) {
469 $where .= $wpdb->prepare( ' AND tt.parent = %d', $parent );
470 }
471
472 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
473 return $wpdb->get_var( $select . $join . $where );
474 }
475
476
477 /**
478 * Gets the number of posts per language in a date, author or post type archive.
479 *
480 * @since 1.2
481 *
482 * @param object $lang PLL_Language instance.
483 * @param array $q WP_Query arguments ( accepted: post_type, m, year, monthnum, day, author, author_name, post_format, post_status ).
484 * @return int
485 */
486 public function count_posts( $lang, $q = array() ) {
487 global $wpdb;
488
489 $q = wp_parse_args( $q, array( 'post_type' => 'post', 'post_status' => 'publish' ) );
490
491 if ( ! is_array( $q['post_type'] ) ) {
492 $q['post_type'] = array( $q['post_type'] );
493 }
494
495 foreach ( $q['post_type'] as $key => $type ) {
496 if ( ! post_type_exists( $type ) ) {
497 unset( $q['post_type'][ $key ] );
498 }
499 }
500
501 if ( empty( $q['post_type'] ) ) {
502 $q['post_type'] = array( 'post' ); // We *need* a post type.
503 }
504
505 $cache_key = 'pll_count_posts_' . md5( maybe_serialize( $q ) );
506 $counts = wp_cache_get( $cache_key, 'counts' );
507
508 if ( false === $counts ) {
509 $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
510 $join = $this->post->join_clause();
511 $where = sprintf( " WHERE post_status = '%s'", esc_sql( $q['post_status'] ) );
512 $where .= sprintf( " AND {$wpdb->posts}.post_type IN ( '%s' )", join( "', '", esc_sql( $q['post_type'] ) ) );
513 $where .= $this->post->where_clause( $this->get_languages_list() );
514 $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
515
516 if ( ! empty( $q['m'] ) ) {
517 $q['m'] = '' . preg_replace( '|[^0-9]|', '', $q['m'] );
518 $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 0, 4 ) );
519 if ( strlen( $q['m'] ) > 5 ) {
520 $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 4, 2 ) );
521 }
522 if ( strlen( $q['m'] ) > 7 ) {
523 $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 6, 2 ) );
524 }
525 }
526
527 if ( ! empty( $q['year'] ) ) {
528 $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", $q['year'] );
529 }
530
531 if ( ! empty( $q['monthnum'] ) ) {
532 $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", $q['monthnum'] );
533 }
534
535 if ( ! empty( $q['day'] ) ) {
536 $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", $q['day'] );
537 }
538
539 if ( ! empty( $q['author_name'] ) ) {
540 $author = get_user_by( 'slug', sanitize_title_for_query( $q['author_name'] ) );
541 if ( $author ) {
542 $q['author'] = $author->ID;
543 }
544 }
545
546 if ( ! empty( $q['author'] ) ) {
547 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $q['author'] );
548 }
549
550 // Filtered taxonomies ( post_format ).
551 foreach ( $this->get_filtered_taxonomies_query_vars() as $tax_qv ) {
552
553 if ( ! empty( $q[ $tax_qv ] ) ) {
554 $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.object_id = {$wpdb->posts}.ID";
555 $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
556 $join .= " INNER JOIN {$wpdb->terms} AS t ON t.term_id = tt.term_id";
557 $where .= $wpdb->prepare( ' AND t.slug = %s', $q[ $tax_qv ] );
558 }
559 }
560
561 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
562 $res = $wpdb->get_results( $select . $join . $where . $groupby, ARRAY_A );
563 foreach ( (array) $res as $row ) {
564 $counts[ $row['term_taxonomy_id'] ] = $row['num_posts'];
565 }
566
567 wp_cache_set( $cache_key, $counts, 'counts' );
568 }
569
570 return empty( $counts[ $lang->term_taxonomy_id ] ) ? 0 : $counts[ $lang->term_taxonomy_id ];
571 }
572
573 /**
574 * Setup the links model based on options
575 *
576 * @since 1.2
577 *
578 * @return object implementing "links_model interface"
579 */
580 public function get_links_model() {
581 $c = array( 'Directory', 'Directory', 'Subdomain', 'Domain' );
582 $class = get_option( 'permalink_structure' ) ? 'PLL_Links_' . $c[ $this->options['force_lang'] ] : 'PLL_Links_Default';
583
584 /**
585 * Filter the links model class to use
586 * /!\ this filter is fired *before* the $polylang object is available
587 *
588 * @since 2.1.1
589 *
590 * @param string $class A class name: PLL_Links_Default, PLL_Links_Directory, PLL_Links_Subdomain, PLL_Links_Domain
591 */
592 $class = apply_filters( 'pll_links_model', $class );
593
594 return new $class( $this );
595 }
596
597 /**
598 * Some backward compatibility with Polylang < 1.8
599 * allows for example to call $polylang->model->get_post_languages( $post_id ) instead of $polylang->model->post->get_language( $post_id )
600 * this works but should be slower than the direct call, thus an error is triggered in debug mode
601 *
602 * @since 1.8
603 *
604 * @param string $func Function name
605 * @param array $args Function arguments
606 */
607 public function __call( $func, $args ) {
608 $f = $func;
609
610 switch ( $func ) {
611 case 'get_object_term':
612 $o = ( false === strpos( $args[1], 'term' ) ) ? 'post' : 'term';
613 break;
614
615 case 'save_translations':
616 case 'delete_translation':
617 case 'get_translations':
618 case 'get_translation':
619 case 'join_clause':
620 $o = ( 'post' == $args[0] || $this->is_translated_post_type( $args[0] ) ) ? 'post' : ( 'term' == $args[0] || $this->is_translated_taxonomy( $args[0] ) ? 'term' : false );
621 unset( $args[0] );
622 break;
623
624 case 'set_post_language':
625 case 'get_post_language':
626 case 'set_term_language':
627 case 'get_term_language':
628 case 'delete_term_language':
629 case 'get_post':
630 case 'get_term':
631 $str = explode( '_', $func );
632 $f = empty( $str[2] ) ? $str[0] : $str[0] . '_' . $str[2];
633 $o = $str[1];
634 break;
635
636 case 'where_clause':
637 case 'get_objects_in_language':
638 $o = $args[1];
639 unset( $args[1] );
640 break;
641 }
642
643 if ( ! empty( $o ) && is_object( $this->$o ) && method_exists( $this->$o, $f ) ) {
644 if ( WP_DEBUG ) {
645 $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
646 $i = 1 + empty( $debug[1]['line'] ); // the file and line are in $debug[2] if the function was called using call_user_func
647
648 trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
649 sprintf(
650 '%1$s was called incorrectly in %4$s on line %5$s: the call to $polylang->model->%1$s() has been deprecated in Polylang 1.8, use PLL()->model->%2$s->%3$s() instead.' . "\nError handler",
651 esc_html( $func ),
652 esc_html( $o ),
653 esc_html( $f ),
654 esc_html( $debug[ $i ]['file'] ),
655 absint( $debug[ $i ]['line'] )
656 )
657 );
658 }
659 return call_user_func_array( array( $this->$o, $f ), $args );
660 }
661
662 $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
663 trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
664 sprintf(
665 'Call to undefined function PLL()->model->%1$s() in %2$s on line %3$s' . "\nError handler",
666 esc_html( $func ),
667 esc_html( $debug[0]['file'] ),
668 absint( $debug[0]['line'] )
669 ),
670 E_USER_ERROR
671 );
672 }
673 }
674