HEX
Server: Apache
System: Linux 162-240-236-42.bluehost.com 3.10.0-1160.114.2.el7.x86_64 #1 SMP Wed Mar 20 15:54:52 UTC 2024 x86_64
User: bt667 (1004)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: //proc/self/cwd/wp-content/plugins/performant-translations/lib/class-performant-translations.php
<?php
/**
 * Compatibility & Implementation for WordPress.
 *
 * @package Performant_Translations
 */

/**
 * Class Performant_Translations.
 */
class Performant_Translations {
	/**
	 * Hook into WordPress.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public static function init() {
		add_filter( 'load_translation_file', array( __CLASS__, 'load_translation_file' ), 100, 2 );

		add_action( 'wp_head', array( __CLASS__, 'add_generator_tag' ) );
		add_action( 'performant_translations_file_written', array( __CLASS__, 'opcache_invalidate' ) );
		add_action( 'upgrader_process_complete', array( __CLASS__, 'upgrader_process_complete' ), 10, 2 );

		// Plugin integrations.
		// TODO: Remove once these plugins do this themselves.
		add_action( 'loco_file_written', array( __CLASS__, 'regenerate_translation_file' ) );
		add_action( 'wpml_st_translation_file_updated', array( __CLASS__, 'regenerate_translation_file' ) );
	}

	/**
	 * Invalidates OPCache for a given file upon write/modification.
	 *
	 * @param string $file File path.
	 * @return void
	 */
	public static function opcache_invalidate( string $file ) {
		wp_opcache_invalidate( $file );
	}

	/**
	 * Filters the file path for loading translations for the given text domain.
	 *
	 * Similar to the {@see 'load_textdomain_mofile'} filter with the difference that
	 * the file path could be for an MO or PHP file.
	 *
	 * @since 6.5.0
	 *
	 * @param string $file   Path to the translation file to load.
	 * @param string $domain The text domain.
	 * @return string Unfiltered path.
	 */
	public static function load_translation_file( $file, $domain ) {
		/**
		 * WP filesystem subclass.
		 *
		 * @var WP_Filesystem_Base $wp_filesystem WP filesystem subclass.
		 */
		global $wp_filesystem;

		if ( ! str_ends_with( $file, '.mo' ) ) {
			return $file;
		}

		/** This filter is documented in lib/class-performant-translations.php */
		$convert = apply_filters( 'performant_translations_convert_files', true );

		if ( ! $convert ) {
			return $file;
		}

		/** This filter is documented in wp-includes/l10n.php */
		$preferred_format = apply_filters( 'translation_file_format', 'php', $domain ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
		if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) {
			$preferred_format = 'php';
		}

		if ( 'php' !== $preferred_format ) {
			return $file;
		}

		$preferred_file = substr_replace( $file, '.l10n.php', - strlen( '.mo' ) );

		if ( file_exists( $preferred_file ) ) {
			return $file;
		}

		$contents = WP_Translation_File::transform( $file, $preferred_format );

		if ( false !== $contents ) {
			if ( ! function_exists( 'WP_Filesystem' ) ) {
				require_once ABSPATH . '/wp-admin/includes/file.php';
			}

			$modir = dirname( $file );

			$write_success = false;

			if ( true === WP_Filesystem() ) {
				$write_success = $wp_filesystem->put_contents( $preferred_file, $contents, FS_CHMOD_FILE );
			} else {
				if ( is_writable( $modir ) ) {
					$write_success = (bool) file_put_contents( $preferred_file, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
				}
			}

			// If file creation within wp-content/plugins or wp-content/themes failed,
			// try creating it in wp-content/languages instead.
			// See https://github.com/swissspidy/performant-translations/issues/108.
			if ( ! $write_success ) {
				$new_location = '';

				if ( str_contains( $modir, WP_PLUGIN_DIR ) ) {
					$new_location = WP_LANG_DIR . '/plugins/' . $preferred_file;
				} elseif ( str_contains( $modir, basename( get_stylesheet_directory() ) ) ) {
					$new_location = WP_LANG_DIR . '/themes/' . basename( get_stylesheet_directory() ) . '-' . $preferred_file;
				}

				if ( '' !== $new_location ) {
					if ( true === WP_Filesystem() ) {
						$wp_filesystem->put_contents( $new_location, $contents, FS_CHMOD_FILE );
					} else {
						if ( is_writable( $modir ) ) {
							(bool) file_put_contents( $new_location, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
						}
					}
				}
			}
		}

		return $file;
	}

	/**
	 * Creates PHP translation files after the translation updates process.
	 *
	 * @global WP_Filesystem_Base $wp_filesystem WP filesystem subclass.
	 *
	 * @param WP_Upgrader $upgrader   WP_Upgrader instance. In other contexts this might be a
	 *                                Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
	 * @param array       $hook_extra {
	 *     Array of bulk item update data.
	 *
	 *     @type string $action       Type of action. Default 'update'.
	 *     @type string $type         Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
	 *     @type bool   $bulk         Whether the update process is a bulk update. Default true.
	 *     @type array  $plugins      Array of the basename paths of the plugins' main files.
	 *     @type array  $themes       The theme slugs.
	 *     @type array  $translations {
	 *         Array of translations update data.
	 *
	 *         @type string $language The locale the translation is for.
	 *         @type string $type     Type of translation. Accepts 'plugin', 'theme', or 'core'.
	 *         @type string $slug     Text domain the translation is for. The slug of a theme/plugin or
	 *                                'default' for core translations.
	 *         @type string $version  The version of a theme, plugin, or core.
	 *     }
	 * }
	 * @return void
	 *
	 * @phpstan-param array{action: string, type: string, bulk: bool, plugins: string[], themes: string[], translations: array<int, array{language: string, type: string, slug: string, version: string}>} $hook_extra
	 */
	public static function upgrader_process_complete( $upgrader, $hook_extra ) {
		/**
		 * WP filesystem subclass.
		 *
		 * @var WP_Filesystem_Base $wp_filesystem WP filesystem subclass.
		 */
		global $wp_filesystem;

		if ( 'translation' !== $hook_extra['type'] || array() === $hook_extra['translations'] ) {
			return;
		}

		foreach ( $hook_extra['translations'] as $translation ) {
			$files = array();
			switch ( $translation['type'] ) {
				case 'plugin':
					$files[] = WP_LANG_DIR . '/plugins/' . $translation['slug'] . '-' . $translation['language'] . '.mo';
					break;
				case 'theme':
					$files[] = WP_LANG_DIR . '/themes/' . $translation['slug'] . '-' . $translation['language'] . '.mo';
					break;
				default:
					$files[] = WP_LANG_DIR . '/' . $translation['language'] . '.mo';
					$files[] = WP_LANG_DIR . '/admin-' . $translation['language'] . '.mo';
					$files[] = WP_LANG_DIR . '/admin-network-' . $translation['language'] . '.mo';
					$files[] = WP_LANG_DIR . '/continents-cities-' . $translation['language'] . '.mo';
					break;
			}

			foreach ( $files as $file ) {
				if ( file_exists( $file ) ) {
					/** This filter is documented in lib/class-performant-translations.php */
					$preferred_format = apply_filters( 'performant_translations_preferred_format', 'php' );
					if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) {
						$preferred_format = 'php';
					}

					/** This filter is documented in wp-includes/l10n.php */
					$preferred_format = apply_filters( 'translation_file_format', $preferred_format, $translation['slug'] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

					$mofile_preferred = $file;

					if ( 'mo' !== $preferred_format ) {
						$mofile_preferred = substr_replace( $file, ".l10n.$preferred_format", -strlen( '.mo' ) );
					}

					/** This filter is documented in lib/class-performant-translations.php */
					$convert = apply_filters( 'performant_translations_convert_files', true );

					if ( 'mo' !== $preferred_format && $convert ) {
						$contents = WP_Translation_File::transform( $file, $preferred_format );

						if ( false === $contents ) {
							return;
						}

						if ( true === $upgrader->fs_connect( array( dirname( $file ) ) ) ) {
							$file_written = $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE );
						} else {
							$file_written = (bool) file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
						}

						if ( $file_written ) {
							/** This action is documented in lib/class-performant-translations.php */
							do_action( 'performant_translations_file_written', $mofile_preferred );
						}
					}
				}
			}
		}
	}

	/**
	 * Adds a <meta> generator tag for the plugin.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public static function add_generator_tag() {
		echo '<meta name="generator" content="performant-translations ' . esc_attr( PERFORMANT_TRANSLATIONS_VERSION ) . '">' . "\n";
	}

	/**
	 * Regenerates a PHP translation file from a given MO file.
	 *
	 * Useful for plugins such as Loco Translate or WPML which generate custom MO files.
	 * Prevents stale PHP files in those cases.
	 *
	 * This compatibility code is added out of courtesy and is not intended
	 * to be merged into WordPress core.
	 *
	 * @codeCoverageIgnore
	 *
	 * @global WP_Filesystem_Base $wp_filesystem WP filesystem subclass.
	 *
	 * @param string $file Path to translation file.
	 * @return void
	 */
	public static function regenerate_translation_file( string $file ) {
		/**
		 * WP filesystem subclass.
		 *
		 * @var WP_Filesystem_Base $wp_filesystem WP filesystem subclass.
		 */
		global $wp_filesystem;

		if ( ! str_ends_with( $file, '.mo' ) ) {
			return;
		}

		/** This filter is documented in lib/class-performant-translations.php */
		$preferred_format = apply_filters( 'performant_translations_preferred_format', 'php' );

		if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) {
			$preferred_format = 'php';
		}

		$mofile_preferred = $file;

		if ( 'mo' !== $preferred_format ) {
			$mofile_preferred = substr_replace( $file, ".l10n.$preferred_format", -strlen( '.mo' ) );
		}

		/** This filter is documented in lib/class-performant-translations.php */
		$convert = apply_filters( 'performant_translations_convert_files', true );

		if ( 'mo' !== $preferred_format && $convert ) {
			$contents = WP_Translation_File::transform( $file, $preferred_format );

			if ( false !== $contents ) {
				if ( ! function_exists( 'WP_Filesystem' ) ) {
					require_once ABSPATH . '/wp-admin/includes/file.php';
				}

				if ( true === WP_Filesystem() ) {
					$file_written = $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE );
				} else {
					$file_written = (bool) file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
				}

				if ( $file_written ) {
					/** This action is documented in lib/class-performant-translations.php */
					do_action( 'performant_translations_file_written', $mofile_preferred );
				}
			}
		}
	}
}