| Server IP : 5.129.245.98 / Your IP : 216.73.216.246 Web Server : Apache/2.4.66 (Debian) System : Linux 1b8c17c336b9 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/html/wp-content/plugins/cyr2lat/src/php/ |
Upload File : |
<?php
/**
* Main class of the plugin.
*
* @package cyr-to-lat
*/
// phpcs:disable Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpUndefinedNamespaceInspection */
/** @noinspection PhpUndefinedClassInspection */
// phpcs:enable Generic.Commenting.DocComment.MissingShort
namespace CyrToLat;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
use CyrToLat\BackgroundProcesses\PostConversionProcess;
use CyrToLat\BackgroundProcesses\TermConversionProcess;
use CyrToLat\Settings\Converter as SettingsConverter;
use CyrToLat\Settings\SystemInfo as SettingsSystemInfo;
use CyrToLat\Settings\Tables as SettingsTables;
use JsonException;
use Polylang;
use SitePress;
use stdClass;
use WP_CLI;
use WP_Error;
use WP_Post;
use wpdb;
use CyrToLat\Settings\Settings;
use CyrToLat\Symfony\Polyfill\Mbstring\Mbstring;
/**
* Class Main
*/
class Main {
/**
* Request type.
*
* @var Request
*/
protected Request $request;
/**
* Plugin settings.
*
* @var Settings
*/
protected Settings $settings;
/**
* Process posts instance.
*
* @var PostConversionProcess|null
*/
protected ?PostConversionProcess $process_all_posts = null;
/**
* Process terms instance.
*
* @var TermConversionProcess|null
*/
protected ?TermConversionProcess $process_all_terms = null;
/**
* Admin Notices instance.
*
* @var AdminNotices
*/
protected AdminNotices $admin_notices;
/**
* Converter instance.
*
* @var Converter|null
*/
protected ?Converter $converter = null;
/**
* WP_CLI instance.
*
* @var WPCli|null
*/
protected ?WPCli $cli = null;
/**
* ACF instance.
*
* @var ACF|null
*/
protected ?ACF $acf = null;
/**
* Flag showing that we are processing a term.
*
* @var bool
*/
private bool $is_term = false;
/**
* Taxonomies saved in pre_insert_term or get_terms_args filter.
*
* @var string[]
*/
private array $taxonomies = [];
/**
* Polylang locale.
*
* @var string|null
*/
private ?string $pll_locale = null;
/**
* WPML locale.
*
* @var string|null
*/
protected ?string $wpml_locale = null;
/**
* WPML languages.
*
* @var array
*/
protected array $wpml_languages = [];
/**
* The current request is frontend.
*
* @var bool|null
*/
protected ?bool $is_frontend = null;
/**
* Init plugin.
*
* @return void
*/
public function init(): void {
add_action( 'plugins_loaded', [ $this, 'init_all' ], - PHP_INT_MAX );
}
/**
* Init all plugin stuffs.
*
* @return void
*/
public function init_all(): void {
$this->load_textdomain();
$this->init_multilingual();
$this->init_classes();
$this->init_hooks();
}
/**
* Load plugin text domain.
*
* @return void
*/
public function load_textdomain(): void {
load_default_textdomain();
load_plugin_textdomain(
'cyr2lat',
false,
dirname( plugin_basename( constant( 'CYR_TO_LAT_FILE' ) ) ) . '/languages/'
);
}
/**
* Init multilingual features.
* It must be first in the init sequence, as we use defined filters internally in our classes.
*
* @return void
*/
protected function init_multilingual(): void {
if ( class_exists( Polylang::class ) ) {
add_filter( 'locale', [ $this, 'pll_locale_filter' ] );
}
if ( class_exists( SitePress::class ) ) {
$this->wpml_locale = $this->get_wpml_locale();
// We cannot use locale filter here
// as WPML reverts locale at PHP_INT_MAX in \WPML\ST\MO\Hooks\LanguageSwitch::filterLocale.
add_filter( 'ctl_locale', [ $this, 'wpml_locale_filter' ], - PHP_INT_MAX );
add_action( 'wpml_language_has_switched', [ $this, 'wpml_language_has_switched' ], 10, 3 );
}
}
/**
* Init other classes.
*
* @return void
*/
protected function init_classes(): void {
( new ErrorHandler() )->init();
$this->request = new Request();
$this->settings = new Settings(
[
'Cyr To Lat' => [
SettingsTables::class,
SettingsConverter::class,
SettingsSystemInfo::class,
],
]
);
$this->admin_notices = new AdminNotices();
$requirements = new Requirements( $this->settings, $this->admin_notices );
if ( ! $requirements->are_requirements_met() ) {
return;
}
$this->process_all_posts = new PostConversionProcess( $this );
$this->process_all_terms = new TermConversionProcess( $this );
$this->converter = new Converter(
$this,
$this->settings,
$this->process_all_posts,
$this->process_all_terms,
$this->admin_notices
);
$this->acf = new ACF( $this->settings );
$this->is_frontend = $this->request->is_frontend();
}
/**
* Init hooks.
*/
protected function init_hooks(): void {
if ( $this->is_frontend ) {
add_action( 'woocommerce_before_template_part', [ $this, 'woocommerce_before_template_part_filter' ] );
add_action( 'woocommerce_after_template_part', [ $this, 'woocommerce_after_template_part_filter' ] );
}
if ( ! $this->request->is_allowed() ) {
return;
}
add_filter( 'sanitize_title', [ $this, 'sanitize_title' ], 9, 3 );
add_filter( 'sanitize_file_name', [ $this, 'sanitize_filename' ], 10, 2 );
add_filter( 'wp_insert_post_data', [ $this, 'sanitize_post_name' ], 10, 2 );
add_filter( 'pre_insert_term', [ $this, 'pre_insert_term_filter' ], PHP_INT_MAX, 2 );
add_filter( 'post_updated', [ $this, 'check_for_changed_slugs' ], 10, 3 );
if ( ! $this->is_frontend || class_exists( SitePress::class ) ) {
add_filter( 'get_terms_args', [ $this, 'get_terms_args_filter' ], PHP_INT_MAX, 2 );
}
add_action( 'before_woocommerce_init', [ $this, 'declare_wc_compatibility' ] );
if ( $this->request->is_cli() ) {
add_action( 'cli_init', [ $this, 'action_cli_init' ] );
}
}
/**
* Action cli init.
*
* @return void
*/
public function action_cli_init(): void {
$this->cli = new WPCli( $this->converter );
/**
* Method WP_CLI::add_command() accepts a class as callable.
*
* @noinspection PhpParamsInspection
*/
WP_CLI::add_command( 'cyr2lat', $this->cli );
}
/**
* Get a Settings instance.
*
* @return Settings
*/
public function settings(): Settings {
return $this->settings;
}
/**
* Sanitize title.
*
* @param string|mixed $title Sanitized title.
* @param string|mixed $raw_title The title prior to sanitization.
* @param string|mixed $context The context for which the title is being sanitized.
*
* @return string|mixed
* @noinspection PhpUnusedParameterInspection
* @noinspection PhpMissingReturnTypeInspection
* @noinspection ReturnTypeCanBeDeclaredInspection
*/
public function sanitize_title( $title, $raw_title = '', $context = '' ) {
global $wpdb;
if (
! $title ||
// Fix the bug with `_wp_old_slug` redirect.
'query' === $context ||
! $this->transliterate_on_pre_term_slug_filter( (string) $title )
) {
return $title;
}
$title = urldecode( (string) $title );
$pre = apply_filters( 'ctl_pre_sanitize_title', false, $title );
if ( false !== $pre ) {
return $pre;
}
if ( $this->is_term ) {
// Make sure we search in the db only once being called from wp_insert_term().
$this->is_term = false;
// Fix a case when showing previously created categories in cyrillic with WPML.
if ( $this->is_frontend && class_exists( SitePress::class ) ) {
return $title;
}
$sql = $wpdb->prepare(
"SELECT slug FROM $wpdb->terms t LEFT JOIN $wpdb->term_taxonomy tt
ON t.term_id = tt.term_id
WHERE t.slug = %s",
rawurlencode( $title )
);
if ( $this->taxonomies ) {
$sql .= ' AND tt.taxonomy IN (' . $this->prepare_in( $this->taxonomies ) . ')';
}
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
$term = $wpdb->get_var( $sql );
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
if ( ! empty( $term ) ) {
return $term;
}
}
return $this->is_wc_attribute( $title ) ? $title : $this->transliterate( $title );
}
/**
* WC before template part filter.
* Add the sanitize_title filter to support transliteration of WC attributes on the frontend.
*
* @return void
*/
public function woocommerce_before_template_part_filter(): void {
add_filter( 'sanitize_title', [ $this, 'sanitize_title' ], 9, 3 );
}
/**
* WC after the template part filter.
* Remove the sanitize_title filter after supporting transliteration of WC attributes on the frontend.
*
* @return void
*/
public function woocommerce_after_template_part_filter(): void {
remove_filter( 'sanitize_title', [ $this, 'sanitize_title' ], 9 );
}
/**
* Check if title is an attribute taxonomy.
*
* @param string $title Title.
*
* @return bool
* @noinspection PhpUndefinedFunctionInspection
*/
protected function is_wc_attribute_taxonomy( string $title ): bool {
$title = preg_replace( '/^pa_/', '', $title );
foreach ( wc_get_attribute_taxonomies() as $attribute_taxonomy ) {
if ( $title === $attribute_taxonomy->attribute_name ) {
return true;
}
}
return false;
}
/**
* Check if the title is a local attribute.
*
* @param string $title Title.
*
* @return bool
*/
protected function is_local_attribute( string $title ): bool {
// Global attribute.
if ( 0 === strpos( $title, 'pa_' ) ) {
return false;
}
// phpcs:disable WordPress.Security.NonceVerification.Missing
$action = (string) filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// The `save attributes` action.
if ( 'woocommerce_save_attributes' === $action ) {
$data = (string) filter_input( INPUT_POST, 'data', FILTER_SANITIZE_URL );
$attributes = $this->wp_parse_str( urldecode( $data ) );
$attribute_names = $attributes['attribute_names'] ?? [];
return in_array( $title, $attribute_names, true );
}
// The `edit post` action.
if ( 'editpost' === $action ) {
$attribute_names = array_map(
'sanitize_text_field',
(array) wp_unslash( $_POST['attribute_names'] ?? [] )
);
return in_array( $title, $attribute_names, true );
}
if ( doing_action( 'woocommerce_variable_add_to_cart' ) ) {
$attributes = $GLOBALS['product']->get_attributes();
$encoded_attr_name = strtolower( rawurlencode( mb_strtolower( $title ) ) );
if ( isset( $attributes[ $encoded_attr_name ] ) ) {
return true;
}
return false;
}
if ( did_action( 'woocommerce_load_cart_from_session' ) ) {
return true;
}
$attr_name = str_replace( 'attribute_', '', mb_strtolower( $title ) );
$attr_name = 'attribute_' . $attr_name;
$encoded_attr_name = rawurlencode( $attr_name );
return isset( $_POST[ $encoded_attr_name ] ) || isset( $_POST[ strtolower( $encoded_attr_name ) ] );
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
// @codeCoverageIgnoreStart
/**
* Polyfill of the wp_parse_str().
* Added for test reasons.
*
* @param string $input_string Input string.
*
* @return array
*/
protected function wp_parse_str( string $input_string ): array {
wp_parse_str( $input_string, $result );
return $result;
}
// @codeCoverageIgnoreEnd
/**
* Check if title is a product not converted attribute.
*
* @param string $title Title.
*
* @return bool
* @noinspection PhpUndefinedFunctionInspection
* @noinspection PhpUndefinedMethodInspection
*/
protected function is_wc_product_not_converted_attribute( string $title ): bool {
global $product;
if ( ! is_a( $product, 'WC_Product' ) ) {
return false;
}
// We have to get attributes from postmeta here to see the converted slug.
$attributes = (array) get_post_meta( $product->get_id(), '_product_attributes', true );
foreach ( $attributes as $slug => $attribute ) {
$name = $attribute['name'] ?? '';
if ( $name === $title && sanitize_title_with_dashes( $title ) === $slug ) {
return true;
}
}
return false;
}
/**
* Check if title is an attribute.
*
* @param string $title Title.
*
* @return bool
* @noinspection PhpUndefinedFunctionInspection
*/
protected function is_wc_attribute( string $title ): bool {
if ( ! function_exists( 'WC' ) ) {
return false;
}
return (
$this->is_wc_attribute_taxonomy( $title ) ||
$this->is_local_attribute( $title ) ||
$this->is_wc_product_not_converted_attribute( $title )
);
}
/**
* Sanitize filename.
*
* @param string|mixed $filename Sanitized filename.
* @param string|mixed $filename_raw The filename prior to sanitization.
*
* @return string
* @noinspection PhpUnusedParameterInspection
* @noinspection PhpMissingReturnTypeInspection
* @noinspection ReturnTypeCanBeDeclaredInspection
*/
public function sanitize_filename( $filename, $filename_raw ) {
global $wp_version;
$pre = apply_filters( 'ctl_pre_sanitize_filename', false, $filename );
if ( false !== $pre ) {
return (string) $pre;
}
$filename = (string) $filename;
$is_utf8 = version_compare( (string) $wp_version, '6.9-RC1', '>=' ) ? 'wp_is_valid_utf8' : 'seems_utf8';
if ( $is_utf8( $filename ) ) {
$filename = (string) Mbstring::mb_strtolower( $filename );
}
return $this->transliterate( $filename );
}
/**
* Get min suffix.
*
* @return string
*/
public function min_suffix(): string {
return defined( 'SCRIPT_DEBUG' ) && constant( 'SCRIPT_DEBUG' ) ? '' : '.min';
}
/**
* Fix string encoding on macOS.
*
* @param string $str String.
* @param array $table Conversion table.
*
* @return string
*/
private function fix_mac_string( string $str, array $table ): string {
$fix_table = ConversionTables::get_fix_table_for_mac();
$fix = [];
foreach ( $fix_table as $key => $value ) {
if ( isset( $table[ $key ] ) ) {
$fix[ $value ] = $table[ $key ];
}
}
return strtr( $str, $fix );
}
/**
* Split Chinese string by hyphens.
*
* @param string $str String.
* @param array $table Conversion table.
*
* @return string
*/
protected function split_chinese_string( string $str, array $table ): string {
if ( ! $this->settings->is_chinese_locale() || mb_strlen( $str ) < 4 ) {
return $str;
}
$chars = Mbstring::mb_str_split( $str );
$str = '';
foreach ( $chars as $char ) {
if ( isset( $table[ $char ] ) ) {
$str .= '-' . $char . '-';
} else {
$str .= $char;
}
}
return $str;
}
/**
* Transliterate string using a table.
*
* @param string $str String.
*
* @return string
*/
public function transliterate( string $str ): string {
$table = (array) apply_filters( 'ctl_table', $this->settings->get_table() );
$str = $this->fix_mac_string( $str, $table );
$str = $this->split_chinese_string( $str, $table );
return strtr( $str, $table );
}
/**
* Check if the Block Editor is active.
* Must only be used after the plugins_loaded action is fired.
*
* @return bool
* @noinspection PhpUndefinedFunctionInspection
*/
private function is_gutenberg_editor_active(): bool {
// Gutenberg plugin is installed and activated.
// This filter was removed in WP 5.5.
if ( has_filter( 'replace_editor', 'gutenberg_init' ) ) {
return true;
}
if ( ! function_exists( 'is_plugin_active' ) ) {
// @codeCoverageIgnoreStart
include_once ABSPATH . 'wp-admin/includes/plugin.php';
// @codeCoverageIgnoreEnd
}
if ( is_plugin_active( 'classic-editor/classic-editor.php' ) ) {
return in_array( get_option( 'classic-editor-replace' ), [ 'no-replace', 'block' ], true );
}
if ( is_plugin_active( 'disable-gutenberg/disable-gutenberg.php' ) ) {
return ! disable_gutenberg();
}
return true;
}
/**
* Gutenberg support
*
* @param array|mixed $data An array of slashed post data.
* @param array|mixed $postarr An array of sanitized, but otherwise unmodified post data.
*
* @return array|mixed
* @noinspection PhpUnusedParameterInspection
*/
public function sanitize_post_name( $data, $postarr = [] ) {
global $current_screen;
if ( ! $this->is_gutenberg_editor_active() ) {
return $data;
}
// Run code only on post-edit screen.
if ( ! ( $current_screen && 'post' === $current_screen->base ) ) {
return $data;
}
if (
! $data['post_name'] && $data['post_title'] &&
! in_array( $data['post_status'], [ 'auto-draft', 'revision' ], true )
) {
$data['post_name'] = sanitize_title( $data['post_title'] );
}
return $data;
}
/**
* Filters a term before it is sanitized and inserted into the database.
*
* @param string|int|WP_Error $term The term name to add, or a WP_Error object if there's an error.
* @param string $taxonomy Taxonomy slug.
*
* @return string|int|WP_Error
*/
public function pre_insert_term_filter( $term, string $taxonomy ) {
if (
0 === $term ||
is_wp_error( $term ) ||
'' === trim( $term )
) {
return $term;
}
$this->is_term = true;
$this->taxonomies = [ $taxonomy ];
return $term;
}
/**
* Filters the term query arguments.
*
* @param array|mixed $args An array of get_terms() arguments.
* @param string[] $taxonomies An array of taxonomy names.
*
* @return array|mixed
*/
public function get_terms_args_filter( $args, array $taxonomies ) {
$this->is_term = true;
$this->taxonomies = $taxonomies;
return $args;
}
/**
* Locale filter for Polylang.
*
* @param string|mixed $locale Locale.
*
* @return string|mixed
*/
public function pll_locale_filter( $locale ) {
if ( $this->pll_locale ) {
return $this->pll_locale;
}
$rest_locale = $this->pll_locale_filter_with_rest();
if ( false === $rest_locale ) {
return $locale;
}
if ( $rest_locale ) {
$this->pll_locale = $rest_locale;
return $this->pll_locale;
}
if ( ! is_admin() ) {
return $locale;
}
if ( ! $this->request->is_post() ) {
return $locale;
}
$pll_get_post_language = $this->pll_locale_filter_with_classic_editor();
if ( $pll_get_post_language ) {
$this->pll_locale = $pll_get_post_language;
return $this->pll_locale;
}
$pll_get_term_language = $this->pll_locale_filter_with_term();
if ( $pll_get_term_language ) {
$this->pll_locale = $pll_get_term_language;
return $this->pll_locale;
}
return $locale;
}
/**
* Locale filter for Polylang with REST request.
*
* @return false|null|string
*/
private function pll_locale_filter_with_rest() {
if ( ! defined( 'REST_REQUEST' ) || ! constant( 'REST_REQUEST' ) ) {
return null;
}
/**
* REST Server.
*
* @var WP_REST_Server $rest_server
*/
$rest_server = rest_get_server();
try {
$data = json_decode( $rest_server::get_raw_data(), false, 512, JSON_THROW_ON_ERROR );
} catch ( JsonException $e ) {
$data = new stdClass();
}
return $data->lang ?? false;
}
/**
* Locale filter for Polylang with the classic editor.
*
* @return bool|string
* @noinspection PhpUndefinedFunctionInspection
*/
private function pll_locale_filter_with_classic_editor() {
if ( ! function_exists( 'pll_get_post_language' ) ) {
return false;
}
$pll_get_post_language = false;
// phpcs:disable WordPress.Security.NonceVerification.Missing
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_POST['post_ID'] ) ) {
$pll_get_post_language = pll_get_post_language(
(int) filter_input( INPUT_POST, 'post_ID', FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
'locale'
);
}
if ( isset( $_POST['pll_post_id'] ) ) {
$pll_get_post_language = pll_get_post_language(
(int) filter_input( INPUT_POST, 'pll_post_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
'locale'
);
}
if ( isset( $_GET['post'] ) ) {
$pll_get_post_language = pll_get_post_language(
(int) filter_input( INPUT_GET, 'post', FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
'locale'
);
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// phpcs:enable WordPress.Security.NonceVerification.Missing
return $pll_get_post_language;
}
/**
* Locale filter for Polylang with term.
*
* @return false|string
* @noinspection PhpUndefinedFunctionInspection
*/
private function pll_locale_filter_with_term() {
if ( ! function_exists( 'PLL' ) ) {
return false;
}
$pll_get_term_language = false;
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['term_lang_choice'] ) ) {
$pll_get_language = PLL()->model->get_language(
filter_input( INPUT_POST, 'term_lang_choice', FILTER_SANITIZE_FULL_SPECIAL_CHARS )
);
if ( $pll_get_language ) {
$pll_get_term_language = $pll_get_language->locale;
}
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
return $pll_get_term_language;
}
/**
* Locale filter for WPML.
*
* @param string|mixed $locale Locale.
*
* @return string|null|mixed
*/
public function wpml_locale_filter( $locale ) {
if ( $this->wpml_locale ) {
return $this->wpml_locale;
}
return $locale;
}
/**
* Get wpml locale.
*
* @return string|null
* @noinspection PhpUndefinedFunctionInspection
*/
protected function get_wpml_locale(): ?string {
$language_code = wpml_get_current_language();
$this->wpml_languages = (array) apply_filters( 'wpml_active_languages', [] );
return $this->wpml_languages[ $language_code ]['default_locale'] ?? null;
}
/**
* Save switched locale.
*
* @param null|string $language_code Language code to switch into.
* @param bool|string $cookie_lang Optionally also switch the cookie language to the value given.
* @param string $original_language Original language.
*
* @return void
* @noinspection PhpUnusedParameterInspection
* @noinspection PhpMissingParamTypeInspection
*/
public function wpml_language_has_switched( $language_code, $cookie_lang, string $original_language ): void {
$language_code = (string) $language_code;
$this->wpml_locale = $this->wpml_languages[ $language_code ]['default_locale'] ?? null;
}
/**
* Checks for changed slugs for published post objects to save the old slug.
*
* @param int $post_id Post ID.
* @param WP_Post $post The post object.
* @param WP_Post $post_before The previous post object.
*
* @noinspection PhpMissingParamTypeInspection
* @noinspection PhpUnusedParameterInspection
*/
public function check_for_changed_slugs( $post_id, $post, $post_before ): void {
// Don't bother if it hasn't changed.
if ( $post->post_name === $post_before->post_name ) {
return;
}
// We're only concerned with published, non-hierarchical objects.
if ( ! ( 'publish' === $post->post_status || ( 'attachment' === get_post_type( $post ) && 'inherit' === $post->post_status ) ) || is_post_type_hierarchical( $post->post_type ) ) {
return;
}
// Modify $post_before->post_name when cyr2lat converted the title.
if (
empty( $post_before->post_name ) &&
$post->post_title !== $post->post_name &&
$post->post_name === $this->transliterate( $post->post_title )
) {
$post_before->post_name = rawurlencode( $post->post_title );
}
}
/**
* Declare compatibility with custom order tables for WooCommerce.
*
* @return void
*/
public function declare_wc_compatibility(): void {
if ( class_exists( FeaturesUtil::class ) ) {
FeaturesUtil::declare_compatibility(
'custom_order_tables',
constant( 'CYR_TO_LAT_FILE' )
);
}
}
/**
* Changes an array of items into a string of items, separated by comma and sql-escaped.
*
* @see https://coderwall.com/p/zepnaw
* @global wpdb $wpdb
*
* @param mixed|array $items item(s) to be joined into string.
* @param string $format %s or %d.
*
* @return string Items separated by comma and sql-escaped.
*/
public function prepare_in( $items, string $format = '%s' ): string {
global $wpdb;
$prepared_in = '';
$items = (array) $items;
$how_many = count( $items );
if ( $how_many > 0 ) {
$placeholders = array_fill( 0, $how_many, $format );
$prepared_format = implode( ',', $placeholders );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$prepared_in = $wpdb->prepare( $prepared_format, $items );
}
return $prepared_in;
}
/**
* Check if we should transliterate the tag on pre_term_slug filter.
*
* @param string $title Title.
*
* @return bool
*/
protected function transliterate_on_pre_term_slug_filter( string $title ): bool {
global $wp_query;
$tag_var = $wp_query->query_vars['tag'] ?? null;
return ! (
$tag_var === $title &&
doing_filter( 'pre_term_slug' ) &&
// Transliterate on pre_term_slug with Polylang and WPML only.
! ( class_exists( 'Polylang' ) || class_exists( 'SitePress' ) )
);
}
}