Map of integration keys to class names */ private static $localization_integrations = [ 'polylang' => Polylang::class, 'wpml' => WPML::class, ]; /** * @var array Cached integration instances */ private static $integration_instances = []; /** * Get all localization integration keys * * @return array Array of integration keys */ public static function get_localization_integration_keys(): array { return array_keys( self::$localization_integrations ); } /** * Get a specific localization integration instance * * @param string $integration_key Integration key (e.g., 'polylang') * @return Abstract_Localization_Integration|null Integration instance or null if not found */ public static function get_localization_integration( string $integration_key ): ?Abstract_Localization_Integration { if ( ! isset( self::$localization_integrations[ $integration_key ] ) ) { return null; } // Return cached instance if available if ( isset( self::$integration_instances[ $integration_key ] ) ) { return self::$integration_instances[ $integration_key ]; } $class_name = self::$localization_integrations[ $integration_key ]; // Verify class exists and extends the abstract base if ( ! class_exists( $class_name ) || ! is_subclass_of( $class_name, Abstract_Localization_Integration::class ) ) { return null; } // Create and cache the instance $instance = new $class_name(); self::$integration_instances[ $integration_key ] = $instance; return $instance; } /** * Get all localization integration instances * * @return array Array of integration instances keyed by integration key */ public static function get_all_localization_integrations(): array { $integrations = []; foreach ( self::get_localization_integration_keys() as $key ) { $integration = self::get_localization_integration( $key ); if ( $integration ) { $integrations[ $key ] = $integration; } } return $integrations; } /** * Get availability data for all localization integrations * * @return array Array of integration availability data keyed by integration key */ public static function get_all_localization_availability_data(): array { $availability_data = []; foreach ( self::get_all_localization_integrations() as $key => $integration ) { $availability_data[ $key ] = self::get_integration_availability_data( $integration ); } return $availability_data; } /** * Get availability data for a specific integration * * @param Abstract_Localization_Integration $integration Integration instance * @return array Integration availability data */ private static function get_integration_availability_data( Abstract_Localization_Integration $integration ): array { // Use the standardized method from the abstract base class return $integration->get_availability_data(); } /** * Register a new localization integration * * @param string $key Integration key * @param string $class_name Integration class name * @return bool True if registered successfully, false otherwise */ public static function register_localization_integration( string $key, string $class_name ): bool { // Verify class exists and extends the abstract base if ( ! class_exists( $class_name ) || ! is_subclass_of( $class_name, Abstract_Localization_Integration::class ) ) { return false; } self::$localization_integrations[ $key ] = $class_name; // Clear cached instance if it exists unset( self::$integration_instances[ $key ] ); return true; } /** * Check if any localization plugin is active and properly configured * * @return bool True if at least one localization plugin is available */ public static function has_active_localization_plugin(): bool { $integrations = self::get_all_localization_integrations(); foreach ( $integrations as $integration ) { if ( $integration->is_available() ) { return true; } } return false; } /** * Get the first active localization integration * * Returns the first integration that has an active plugin (regardless of configuration). * This is useful for getting language information even if the integration isn't fully configured. * * **Multiple Localization Plugins:** * Integrations are checked in the order they appear in $localization_integrations array: * 1. Polylang (checked first) * 2. WPML (checked second) * * If both plugins are somehow active, only Polylang's integration will be returned and used. * * **In Practice:** * Based on telemetry data from Meta for WooCommerce users, ZERO sites have both * WPML and Polylang activated simultaneously. This is because: * - WPML throws a fatal error during initialization if Polylang is already active * - If Polylang is activated after WPML, WPML's functionality is disabled * * **Plugin Conflicts:** * The order of precedence (Polylang first) is intentional but rarely matters in practice * due to the plugin conflicts mentioned above. If conflicts are resolved in future versions * of these plugins, this method will consistently return Polylang when both are active. * * @return Abstract_Localization_Integration|null The first active integration or null if none active * @since 3.6.0 */ public static function get_active_localization_integration(): ?Abstract_Localization_Integration { $integrations = self::get_all_localization_integrations(); foreach ( $integrations as $integration ) { if ( $integration->is_plugin_active() ) { return $integration; } } return null; } /** * Clear all cached integration instances * * Useful for testing or when integration states might have changed */ public static function clear_cache(): void { self::$integration_instances = []; } /** * Get list of active plugin names * * @return array Array of active plugin names */ public static function get_all_active_plugin_data(): array { try { if ( ! function_exists( 'get_plugins' ) ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; } $active_plugins_list = get_option( 'active_plugins', [] ); $all_plugins = get_plugins(); $active_plugins_data = []; foreach ( $active_plugins_list as $plugin_file ) { if ( isset( $all_plugins[ $plugin_file ] ) ) { $plugin_data = $all_plugins[ $plugin_file ]; $active_plugins_data[] = $plugin_data['Name']; } } return $active_plugins_data; } catch ( \Exception $e ) { // Log error but return empty array to prevent breaking the update process if ( class_exists( 'WooCommerce\Facebook\Framework\Logger' ) ) { \WooCommerce\Facebook\Framework\Logger::log( 'Error getting active plugin data: ' . $e->getMessage(), [], array( 'should_send_log_to_meta' => false, 'should_save_log_in_woocommerce' => true, 'woocommerce_log_level' => \WC_Log_Levels::ERROR, ) ); } return []; } } }