locales = $locales; } public function register() { // LiteSpeed Cache keeps one cached copy per cookie value. add_filter( 'litespeed_vary_cookies', array( $this, 'cache_cookies' ) ); add_filter( 'litespeed_vary_curr_cookies', array( $this, 'cache_cookies' ) ); add_action( 'admin_init', array( $this, 'register_super_cache_cookie' ) ); add_action( 'template_redirect', array( $this, 'prevent_storing_cookie_variant' ), 2 ); add_action( 'add_option_' . LocaleManager::OPTION, array( $this, 'settings_added' ), 10, 2 ); add_action( 'update_option_' . LocaleManager::OPTION, array( $this, 'settings_updated' ), 10, 2 ); } public function cache_cookies( $cookies ) { $cookies = is_array( $cookies ) ? $cookies : array(); if ( $this->locales->standalone_available() && ! in_array( StandaloneLocale::COOKIE, $cookies, true ) ) { $cookies[] = StandaloneLocale::COOKIE; } return $cookies; } /** * WP Super Cache stores a separate copy per cookie value once told about it. * Re-adding is a no-op, and it restores the cookie after a cache config reset. */ public function register_super_cache_cookie() { if ( $this->locales->standalone_available() ) { do_action( 'wpsc_add_cookie', StandaloneLocale::COOKIE ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WP Super Cache's documented API action. } $this->sync_w3tc_cookie( $this->locales->standalone_available() ); $this->sync_litespeed_vary( $this->locales->standalone_available() ); } /** * LiteSpeed Cache adds the cookies from litespeed_vary_cookies to its * .htaccess "Cache-Vary" rule only when it regenerates its rules, normally * when its own settings are saved. Until then the server keeps one copy per * URL and serves the default-language page to every visitor. Saving its * settings through its public API regenerates the rule; this runs once per * switcher change and LiteSpeed Cache version. * * @param bool $enabled Whether the standalone switcher is active. */ public function sync_litespeed_vary( $enabled ) { if ( ! defined( 'LSCWP_V' ) ) { return; } $state = LSCWP_V . '|' . ( $enabled ? '1' : '0' ); if ( get_option( self::LITESPEED_OPTION ) === $state ) { return; } do_action( 'litespeed_save_conf' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- LiteSpeed Cache's documented API action. update_option( self::LITESPEED_OPTION, $state, false ); update_option( self::LITESPEED_CHANGED, (string) microtime( true ), false ); } /** * Removes the language cookie from the page caches when Glovario is deactivated. */ public static function deactivate() { do_action( 'wpsc_delete_cookie', StandaloneLocale::COOKIE ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WP Super Cache's documented API action. $compat = new self( new StandaloneLocale( new LocaleManager( new \Glovario\Repository\RuleRepository() ) ) ); $compat->sync_w3tc_cookie( false ); if ( defined( 'LSCWP_V' ) ) { add_filter( 'litespeed_vary_cookies', static function ( $cookies ) { return array_values( array_diff( (array) $cookies, array( StandaloneLocale::COOKIE ) ) ); }, PHP_INT_MAX ); do_action( 'litespeed_save_conf' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- LiteSpeed Cache's documented API action. } delete_option( self::LITESPEED_OPTION ); delete_option( self::LITESPEED_CHANGED ); } /** * W3 Total Cache serves cached pages before plugins load and keys them by * URL. A cookie group per second language ("glovario_it_it" matching * glovario_locale=it_IT) makes it keep a separate, cached copy for that language. * * @param bool $enabled Whether the standalone switcher is active. */ public function sync_w3tc_cookie( $enabled ) { if ( ! function_exists( 'w3tc_config' ) ) { return; } try { $config = w3tc_config(); $groups = (array) $config->get_array( 'pgcache.cookiegroups.groups' ); $wanted = array(); $second = $this->locales->manager()->settings()['second_locale']; if ( $enabled && '' !== $second ) { $wanted[ self::w3tc_group( $second ) ] = array( 'enabled' => true, 'cache' => true, 'cookies' => array( StandaloneLocale::COOKIE . '=' . $second ), ); } $updated = array_merge( array_filter( $groups, static function ( $name ) { return 0 !== strpos( (string) $name, 'glovario_' ); }, ARRAY_FILTER_USE_KEY ), $wanted ); // Disk: Enhanced serves cached files from rewrite rules that ignore cookie // groups, so the cookie is rejected there instead: visitors who chose the // second language skip the static files and get pages from WordPress. $enhanced = self::w3tc_enhanced( $config ); $rejected = (array) $config->get_array( 'pgcache.reject.cookie' ); $kept = array_values( array_diff( $rejected, array( StandaloneLocale::COOKIE ) ) ); if ( $wanted && $enhanced ) { $kept[] = StandaloneLocale::COOKIE; } if ( $updated === $groups && $kept === $rejected && ( ! $wanted || $config->get_boolean( 'pgcache.cookiegroups.enabled' ) ) ) { return; } $config->set( 'pgcache.cookiegroups.groups', $updated ); $config->set( 'pgcache.reject.cookie', $kept ); if ( $wanted ) { $config->set( 'pgcache.cookiegroups.enabled', true ); } $config->save(); if ( $kept !== $rejected && $enhanced && class_exists( '\W3TC\Dispatcher' ) ) { // Rewrite its .htaccess rules with the new list, as its fix_environment command does. \W3TC\Dispatcher::component( 'PgCache_Environment' )->fix_on_wpadmin_request( $config, true ); } } catch ( \Exception $exception ) { do_action( 'glovario_multilingual_integration_error', 'w3-total-cache', $exception ); } } /** * The caching proxy or CDN in front of the site, recognised from the * headers it adds to the current (admin) request; no network request. * * @return string Its name, or an empty string. */ public static function proxy_in_front() { $header = static function ( $name ) { return isset( $_SERVER[ $name ] ) ? sanitize_text_field( wp_unslash( $_SERVER[ $name ] ) ) : ''; }; $proxy = ''; if ( '' !== $header( 'HTTP_CF_RAY' ) ) { $proxy = 'Cloudflare'; } elseif ( '' !== $header( 'HTTP_FASTLY_CLIENT_IP' ) || false !== stripos( $header( 'HTTP_CDN_LOOP' ), 'fastly' ) ) { $proxy = 'Fastly'; } elseif ( '' !== $header( 'HTTP_X_VARNISH' ) || false !== stripos( $header( 'HTTP_VIA' ), 'varnish' ) ) { $proxy = 'Varnish'; } /** * Filters the caching proxy or CDN reported in front of the site; Locale * Settings tells Standalone-mode sites how to keep it from serving one * language's cached page to visitors who chose the other. * * @param string $proxy Name, or an empty string when none is recognised. */ return (string) apply_filters( 'glovario_proxy_cache_in_front', $proxy ); } private static function w3tc_enhanced( $config ) { return $config->get_boolean( 'pgcache.enabled' ) && 'file_generic' === $config->get_string( 'pgcache.engine' ); } private static function w3tc_group( $locale ) { return 'glovario_' . strtolower( sanitize_key( $locale ) ); } /** * Whether an active page cache keeps a separate copy for this language. * * @param string $locale Locale selected by the cookie. * @return bool */ public function cache_keeps_language_copies( $locale ) { $keeps = false; if ( defined( 'LSCWP_V' ) && get_option( self::LITESPEED_OPTION ) === LSCWP_V . '|1' ) { $keeps = true; // Its vary rule includes the cookie (see sync_litespeed_vary). } if ( ! empty( $GLOBALS['wpsc_cookies'] ) && in_array( StandaloneLocale::COOKIE, (array) $GLOBALS['wpsc_cookies'], true ) ) { $keeps = true; } if ( function_exists( 'w3tc_config' ) ) { try { $config = w3tc_config(); $groups = (array) $config->get_array( 'pgcache.cookiegroups.groups' ); // Disk: Enhanced rejects the cookie (see sync_w3tc_cookie), so no copy is kept there. $keeps = $keeps || ( ! self::w3tc_enhanced( $config ) && $config->get_boolean( 'pgcache.cookiegroups.enabled' ) && ! empty( $groups[ self::w3tc_group( $locale ) ]['enabled'] ) ); } catch ( \Exception $exception ) { do_action( 'glovario_multilingual_integration_error', 'w3-total-cache', $exception ); } } /** * Filters whether the active page cache stores one copy per language * cookie value. Return true for a cache configured to vary on glovario_locale, * so pages in the second language are cached instead of rebuilt. * * @param bool $keeps Whether a known cache keeps language copies. * @param string $locale Locale selected by the cookie. */ return (bool) apply_filters( 'glovario_cache_varies_by_language_cookie', $keeps, $locale ); } public function prevent_storing_cookie_variant() { $locale = $this->locales->cookie_variant_locale(); if ( is_admin() || wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || '' === $locale || $this->cache_keeps_language_copies( $locale ) ) { return; } if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- The shared page-cache constant that cache plugins read. } if ( ! headers_sent() ) { nocache_headers(); } do_action( 'litespeed_control_set_nocache', self::NOCACHE_REASON ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- LiteSpeed Cache's documented API action. } public function settings_added( $option, $settings ) { $this->settings_updated( array(), $settings ); } public function settings_updated( $previous, $settings ) { // The site-wide flag is part of the cached HTML, so switching it on or off purges too. $fields = array( 'mode', 'default_locale', 'second_locale', 'global_switcher' ); $previous = array_intersect_key( is_array( $previous ) ? $previous : array(), array_flip( $fields ) ); $settings = array_intersect_key( is_array( $settings ) ? $settings : array(), array_flip( $fields ) ); if ( $previous === $settings ) { return; } if ( $this->locales->standalone_available() ) { do_action( 'wpsc_add_cookie', StandaloneLocale::COOKIE ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WP Super Cache's documented API action. } else { do_action( 'wpsc_delete_cookie', StandaloneLocale::COOKIE ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WP Super Cache's documented API action. } $this->sync_w3tc_cookie( $this->locales->standalone_available() ); $this->sync_litespeed_vary( $this->locales->standalone_available() ); // Pages cached under the previous locale configuration may show the wrong language or flag. PageCachePurger::purge( 'locale_settings', array( 'settings' => $settings, 'previous' => $previous, ) ); } }