HEX
Server: Apache
System: Linux b5.s-host.com.ua 4.18.0-305.10.2.el7.x86_64 #1 SMP Fri Jul 23 21:00:55 UTC 2021 x86_64
User: unelbhzm (1470)
PHP: 8.0.18
Disabled: NONE
Upload Files
File: /sites/nuofama.com/wp-content/plugins/cyr2lat/src/php/class-main.php
<?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 Cyr_To_Lat;

use Automattic\WooCommerce\Utilities\FeaturesUtil;
use Polylang;
use SitePress;
use WP_Error;
use wpdb;
use Exception;
use Cyr_To_Lat\Settings\Settings;
use Cyr_To_Lat\Symfony\Polyfill\Mbstring\Mbstring;

/**
 * Class Main
 */
class Main {

	/**
	 * Request type.
	 *
	 * @var Request
	 */
	protected $request;

	/**
	 * Plugin settings.
	 *
	 * @var Settings
	 */
	protected $settings;

	/**
	 * Process posts instance.
	 *
	 * @var Post_Conversion_Process
	 */
	protected $process_all_posts;

	/**
	 * Process terms instance.
	 *
	 * @var Term_Conversion_Process
	 */
	protected $process_all_terms;

	/**
	 * Admin Notices instance.
	 *
	 * @var Admin_Notices
	 */
	protected $admin_notices;

	/**
	 * Converter instance.
	 *
	 * @var Converter
	 */
	protected $converter;

	/**
	 * WP_CLI instance.
	 *
	 * @var WP_CLI
	 */
	protected $cli;

	/**
	 * ACF instance.
	 *
	 * @var ACF
	 */
	protected $acf;

	/**
	 * Flag showing that we are processing a term.
	 *
	 * @var bool
	 */
	private $is_term = false;

	/**
	 * Taxonomies saved in pre_insert_term or get_terms_args filter.
	 *
	 * @var string[]|null
	 */
	private $taxonomies;

	/**
	 * Polylang locale.
	 *
	 * @var string
	 */
	private $pll_locale;

	/**
	 * WPML locale.
	 *
	 * @var string
	 */
	protected $wpml_locale;

	/**
	 * WPML languages.
	 *
	 * @var array
	 */
	protected $wpml_languages;

	/**
	 * Current request is frontend.
	 *
	 * @var bool|null
	 */
	protected $is_frontend;

	/**
	 * Main constructor.
	 */
	public function __construct() {
		$this->request       = new Request();
		$this->settings      = new Settings();
		$this->admin_notices = new Admin_Notices();
		$requirements        = new Requirements( $this->settings, $this->admin_notices );

		if ( ! $requirements->are_requirements_met() ) {
			return;
		}

		$this->process_all_posts = new Post_Conversion_Process( $this );
		$this->process_all_terms = new Term_Conversion_Process( $this );
		$this->converter         = new Converter(
			$this,
			$this->settings,
			$this->process_all_posts,
			$this->process_all_terms,
			$this->admin_notices
		);

		if ( $this->request->is_cli() ) {
			$this->cli = new WP_CLI( $this->converter );
		}

		$this->acf         = new ACF( $this->settings );
		$this->is_frontend = $this->request->is_frontend();
	}

	/**
	 * Init class.
	 *
	 * @noinspection PhpUndefinedClassInspection
	 */
	public function init() {
		if ( $this->request->is_cli() ) {
			try {
				/**
				 * Method WP_CLI::add_command() accepts class as callable.
				 *
				 * @noinspection PhpParamsInspection
				 */
				\WP_CLI::add_command( 'cyr2lat', $this->cli );
			} catch ( Exception $ex ) {
				return;
			}
		}

		$this->init_hooks();
	}

	/**
	 * Init class hooks.
	 */
	public function init_hooks() {
		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 );

		if ( ! $this->is_frontend || class_exists( SitePress::class ) ) {
			add_filter( 'get_terms_args', [ $this, 'get_terms_args_filter' ], PHP_INT_MAX, 2 );
		}

		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 );
		}

		add_action( 'before_woocommerce_init', [ $this, 'declare_wc_compatibility' ] );
	}

	/**
	 * Sanitize title.
	 *
	 * @param string $title     Sanitized title.
	 * @param string $raw_title The title prior to sanitization.
	 * @param string $context   The context for which the title is being sanitized.
	 *
	 * @return string
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function sanitize_title( $title, $raw_title = '', $context = '' ) {
		global $wpdb;

		if ( ! $title ) {
			return $title;
		}

		// Fixed bug with `_wp_old_slug` redirect.
		if ( 'query' === $context ) {
			return $title;
		}

		$title = urldecode( $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 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.name = %s",
				$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_taxonomy( $title ) ? $title : $this->transliterate( $title );
	}

	/**
	 * Check if title is an attribute taxonomy.
	 *
	 * @param string $title Title.
	 *
	 * @return bool
	 */
	protected function is_wc_attribute_taxonomy( $title ) {
		if ( ! function_exists( 'wc_get_attribute_taxonomies' ) ) {
			return false;
		}

		$title = str_replace( 'pa_', '', $title );

		$attribute_taxonomies = wc_get_attribute_taxonomies();

		foreach ( $attribute_taxonomies as $attribute_taxonomy ) {
			if ( $title === $attribute_taxonomy->attribute_name ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Sanitize filename.
	 *
	 * @param string $filename     Sanitized filename.
	 * @param string $filename_raw The filename prior to sanitization.
	 *
	 * @return string
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function sanitize_filename( $filename, $filename_raw ) {
		$pre = apply_filters( 'ctl_pre_sanitize_filename', false, $filename );

		if ( false !== $pre ) {
			return $pre;
		}

		if ( seems_utf8( $filename ) ) {
			$filename = (string) Mbstring::mb_strtolower( $filename );
		}

		return $this->transliterate( $filename );
	}

	/**
	 * Fix string encoding on macOS.
	 *
	 * @param string $string String.
	 * @param array  $table  Conversion table.
	 *
	 * @return string
	 */
	private function fix_mac_string( $string, $table ) {
		$fix_table = Conversion_Tables::get_fix_table_for_mac();

		$fix = [];
		foreach ( $fix_table as $key => $value ) {
			if ( isset( $table[ $key ] ) ) {
				$fix[ $value ] = $table[ $key ];
			}
		}

		return strtr( $string, $fix );
	}

	/**
	 * Split Chinese string by hyphens.
	 *
	 * @param string $string String.
	 * @param array  $table  Conversion table.
	 *
	 * @return string
	 */
	protected function split_chinese_string( $string, $table ) {
		if ( ! $this->settings->is_chinese_locale() || mb_strlen( $string ) < 4 ) {
			return $string;
		}

		$chars  = Mbstring::mb_str_split( $string );
		$string = '';

		foreach ( $chars as $char ) {
			if ( isset( $table[ $char ] ) ) {
				$string .= '-' . $char . '-';
			} else {
				$string .= $char;
			}
		}

		return $string;
	}

	/**
	 * Transliterate string using a table.
	 *
	 * @param string $string String.
	 *
	 * @return string
	 */
	public function transliterate( $string ) {
		$table = (array) apply_filters( 'ctl_table', $this->settings->get_table() );

		$string = $this->fix_mac_string( $string, $table );
		$string = $this->split_chinese_string( $string, $table );

		return strtr( $string, $table );
	}

	/**
	 * Check if Classic Editor plugin is active.
	 *
	 * @link https://kagg.eu/how-to-catch-gutenberg/
	 *
	 * @return bool
	 */
	private function is_classic_editor_plugin_active() {
		// @codeCoverageIgnoreStart
		if ( ! function_exists( 'is_plugin_active' ) ) {
			include_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		// @codeCoverageIgnoreEnd

		return is_plugin_active( 'classic-editor/classic-editor.php' );
	}

	/**
	 * Check if Block Editor is active.
	 * Must only be used after plugins_loaded action is fired.
	 *
	 * @link https://kagg.eu/how-to-catch-gutenberg/
	 *
	 * @return bool
	 */
	private function is_gutenberg_editor_active() {

		// Gutenberg plugin is installed and activated.
		$gutenberg = ! ( false === has_filter( 'replace_editor', 'gutenberg_init' ) );

		// Block editor since 5.0.
		$block_editor = version_compare( $GLOBALS['wp_version'], '5.0-beta', '>' );

		if ( ! $gutenberg && ! $block_editor ) {
			return false;
		}

		if ( $this->is_classic_editor_plugin_active() ) {
			$editor_option       = get_option( 'classic-editor-replace' );
			$block_editor_active = [ 'no-replace', 'block' ];

			return in_array( $editor_option, $block_editor_active, true );
		}

		return true;
	}

	/**
	 * Gutenberg support
	 *
	 * @param array $data    An array of slashed post data.
	 * @param array $postarr An array of sanitized, but otherwise unmodified post data.
	 *
	 * @return array
	 * @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
	 */
	public function pre_insert_term_filter( $term, $taxonomy ) {
		if (
			0 === $term ||
			is_wp_error( $term ) ||
			'' === trim( $term )
		) {
			return $term;
		}

		$this->is_term    = true;
		$this->taxonomies = [ $taxonomy ];

		return $term;
	}

	/**
	 * Filters the terms query arguments.
	 *
	 * @param array    $args       An array of get_terms() arguments.
	 * @param string[] $taxonomies An array of taxonomy names.
	 */
	public function get_terms_args_filter( $args, $taxonomies ) {
		$this->is_term    = true;
		$this->taxonomies = $taxonomies;

		return $args;
	}

	/**
	 * Locale filter for Polylang.
	 *
	 * @param string $locale Locale.
	 *
	 * @return string
	 */
	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 = rest_get_server();
		$data        = json_decode( $rest_server::get_raw_data(), false );
		if ( isset( $data->lang ) ) {
			return $data->lang;
		}

		return false;
	}

	/**
	 * Locale filter for Polylang with classic editor.
	 *
	 * @return bool|string
	 */
	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
	 */
	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 $locale Locale.
	 *
	 * @return string
	 */
	public function wpml_locale_filter( $locale ) {
		if ( $this->wpml_locale ) {
			return $this->wpml_locale;
		}

		return $locale;
	}

	/**
	 * Get wpml locale.
	 *
	 * @return string|null
	 */
	protected function get_wpml_locale() {
		$language_code        = wpml_get_current_language();
		$this->wpml_languages = (array) apply_filters( 'wpml_active_languages', [] );

		return (
		isset( $this->wpml_languages[ $language_code ] ) ?
			$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.
	 *
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function wpml_language_has_switched( $language_code, $cookie_lang, $original_language ) {
		$language_code = (string) $language_code;

		$this->wpml_locale =
			isset( $this->wpml_languages[ $language_code ] ) ?
				$this->wpml_languages[ $language_code ]['default_locale'] :
				null;
	}

	/**
	 * Declare compatibility with custom order tables for WooCommerce.
	 *
	 * @return void
	 */
	public function declare_wc_compatibility() {
		if ( class_exists( FeaturesUtil::class ) ) {
			FeaturesUtil::declare_compatibility(
				'custom_order_tables',
				constant( 'CYR_TO_LAT_FILE' ),
				true
			);
		}
	}

	/**
	 * Changes array of items into 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, $format = '%s' ) {
		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;
	}
}