array(self::ARM_CONTROL, self::ARM_TREATMENT), 'semantic_related_diversity' => array('cap2', 'cap0', 'covis'), ); private static $arm = null; public static function config() { $stored = get_option(self::OPTION, array()); $stored = is_array($stored) ? $stored : array(); $subject = isset($stored['subject']) ? (string) $stored['subject'] : ''; if (!in_array($subject, self::SUBJECTS, true)) { $subject = self::SUBJECTS[0]; } $split = isset($stored['split']) ? (int) $stored['split'] : 50; $split = max(5, min(95, $split)); return array( 'enabled' => !empty($stored['enabled']), 'subject' => $subject, 'split' => $split, 'salt' => isset($stored['salt']) ? (string) $stored['salt'] : '', 'started_at' => isset($stored['started_at']) ? (string) $stored['started_at'] : '', ); } public static function is_running() { $c = self::config(); return $c['enabled'] && '' !== $c['salt']; } public static function start($subject = '', $split = 50) { $subject = in_array($subject, self::SUBJECTS, true) ? $subject : self::SUBJECTS[0]; $config = array( 'enabled' => true, 'subject' => $subject, 'split' => max(5, min(95, (int) $split)), 'salt' => wp_generate_password(16, false, false), 'started_at' => gmdate('Y-m-d H:i:s'), ); update_option(self::OPTION, $config, false); self::$arm = null; self::flush_reco_caches(); return self::config(); } public static function stop() { $config = self::config(); $config['enabled'] = false; update_option(self::OPTION, $config, false); self::$arm = null; self::flush_reco_caches(); return self::config(); } private static function flush_reco_caches() { if (class_exists('AIEO_Recommendations_Cache') && method_exists('AIEO_Recommendations_Cache', 'delete_all')) { AIEO_Recommendations_Cache::delete_all(); } } public static function arm() { if (null !== self::$arm) { return self::$arm; } $config = self::config(); if (!$config['enabled'] || '' === $config['salt']) { return self::$arm = self::ARM_NONE; } $session = self::session_id(); if ('' === $session) { return self::$arm = self::ARM_NONE; } $arms = self::arms_for($config['subject']); if (count($arms) > 2) { return self::$arm = self::assign_variant($session, $config['salt'], $arms); } return self::$arm = self::assign($session, $config['salt'], $config['split']); } public static function arms_for($subject) { $sets = self::ARM_SETS; return isset($sets[$subject]) ? $sets[$subject] : array(self::ARM_CONTROL, self::ARM_TREATMENT); } public static function assign($session_id, $salt, $split = 50) { $bucket = crc32($session_id . $salt) % 100; return ($bucket < (int) $split) ? self::ARM_TREATMENT : self::ARM_CONTROL; } public static function assign_variant($session_id, $salt, array $arms) { $n = count($arms); if ($n < 1) { return self::ARM_NONE; } $bucket = crc32($session_id . $salt) % 100; $idx = (int) floor($bucket * $n / 100); return $arms[min($idx, $n - 1)]; } public static function related_diversity_arm() { $config = self::config(); if (!$config['enabled'] || '' === $config['salt'] || self::SUBJECT_DIVERSITY !== $config['subject']) { return ''; } return self::arm(); } public static function cache_suffix() { if (!self::is_running()) { return ''; } $arm = self::arm(); if (self::ARM_NONE === $arm) { return ''; } $arm = preg_replace('/[^a-z0-9]/', '', strtolower($arm)); return ('' === $arm) ? '' : '_a' . $arm; } public static function feature_active($subject) { $config = self::config(); if (!$config['enabled'] || '' === $config['salt'] || $config['subject'] !== $subject) { return true; } return self::ARM_TREATMENT === self::arm(); } private static function session_id() { if (!class_exists('AIEO_Attribution')) { return ''; } if (class_exists('AIEO_Session_Tracker') && method_exists('AIEO_Session_Tracker', 'is_consent_blocking_writes') && AIEO_Session_Tracker::is_consent_blocking_writes()) { return ''; } return (string) AIEO_Attribution::read_visitor_key(); } public static function reset_memo() { self::$arm = null; } }