From df67724db98cb2645178ac4376a393c4c7439263 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Sun, 22 Aug 2021 05:43:42 +0500 Subject: [PATCH 1/9] config v2 implementation --- .../Config/DatafileProjectConfig.php | 117 ++++- .../Config/ProjectConfigInterface.php | 31 ++ .../OptimizelyConfig/OptimizelyAttribute.php | 60 +++ .../OptimizelyConfig/OptimizelyAudience.php | 75 +++ .../OptimizelyConfig/OptimizelyConfig.php | 80 ++- .../OptimizelyConfigService.php | 236 ++++++++- .../OptimizelyConfig/OptimizelyEvent.php | 75 +++ .../OptimizelyConfig/OptimizelyExperiment.php | 18 +- .../OptimizelyConfig/OptimizelyFeature.php | 36 +- .../OptimizelyConfigServiceTest.php | 205 +++++++- .../OptimizelyEntitiesTest.php | 20 +- tests/TestData.php | 479 +++++++++++++++--- 12 files changed, 1313 insertions(+), 119 deletions(-) create mode 100644 src/Optimizely/OptimizelyConfig/OptimizelyAttribute.php create mode 100644 src/Optimizely/OptimizelyConfig/OptimizelyAudience.php create mode 100644 src/Optimizely/OptimizelyConfig/OptimizelyEvent.php diff --git a/src/Optimizely/Config/DatafileProjectConfig.php b/src/Optimizely/Config/DatafileProjectConfig.php index 5f071b5b..42d16dfa 100644 --- a/src/Optimizely/Config/DatafileProjectConfig.php +++ b/src/Optimizely/Config/DatafileProjectConfig.php @@ -93,6 +93,16 @@ class DatafileProjectConfig implements ProjectConfigInterface */ private $datafile; + /** + * @var string environmentKey of the config. + */ + private $environmentKey; + + /** + * @var string sdkKey of the config. + */ + private $sdkKey; + /** * @var string Revision of the datafile. */ @@ -172,6 +182,34 @@ class DatafileProjectConfig implements ProjectConfigInterface */ private $_rollouts; + /** + * list of Attributes that will be parsed from the datafile + * + * @var [Attribute] + */ + private $attributes; + + /** + * list of Audiences that will be parsed from the datafile + * + * @var [Audience] + */ + private $audiences; + + /** + * list of Events that will be parsed from the datafile + * + * @var [Event] + */ + private $events; + + /** + * list of Typed Audiences that will be parsed from the datafile + * + * @var [typed_audience] + */ + private $typedAudiences; + /** * internal mapping of feature keys to feature flag models. * @@ -222,6 +260,8 @@ public function __construct($datafile, $logger, $errorHandler) $this->_logger = $logger; $this->_errorHandler = $errorHandler; $this->_version = $config['version']; + $this->environmentKey = isset($config['environmentKey']) ? $config['environmentKey'] : ''; + $this->sdkKey = isset($config['sdkKey']) ? $config['sdkKey'] : ''; if (!in_array($this->_version, $supportedVersions)) { throw new InvalidDatafileVersionException( "This version of the PHP SDK does not support the given datafile version: {$this->_version}." @@ -230,17 +270,17 @@ public function __construct($datafile, $logger, $errorHandler) $this->_accountId = $config['accountId']; $this->_projectId = $config['projectId']; - $this->_anonymizeIP = isset($config['anonymizeIP'])? $config['anonymizeIP'] : false; - $this->_botFiltering = isset($config['botFiltering'])? $config['botFiltering'] : null; + $this->attributes = isset($config['attributes']) ? $config['attributes'] : []; + $this->audiences = isset($config['audiences']) ? $config['audiences'] : []; + $this->events = $config['events'] ?: []; + $this->typedAudiences = isset($config['typedAudiences']) ? $config['typedAudiences'] : []; + $this->_anonymizeIP = isset($config['anonymizeIP']) ? $config['anonymizeIP'] : false; + $this->_botFiltering = isset($config['botFiltering']) ? $config['botFiltering'] : null; $this->_revision = $config['revision']; $this->_sendFlagDecisions = isset($config['sendFlagDecisions']) ? $config['sendFlagDecisions'] : false; $groups = $config['groups'] ?: []; $experiments = $config['experiments'] ?: []; - $events = $config['events'] ?: []; - $attributes = $config['attributes'] ?: []; - $audiences = $config['audiences'] ?: []; - $typedAudiences = isset($config['typedAudiences']) ? $config['typedAudiences']: []; $rollouts = isset($config['rollouts']) ? $config['rollouts'] : []; $featureFlags = isset($config['featureFlags']) ? $config['featureFlags']: []; @@ -258,10 +298,10 @@ public function __construct($datafile, $logger, $errorHandler) $this->_groupIdMap = ConfigParser::generateMap($groups, 'id', Group::class); $this->_experimentIdMap = ConfigParser::generateMap($experiments, 'id', Experiment::class); - $this->_eventKeyMap = ConfigParser::generateMap($events, 'key', Event::class); - $this->_attributeKeyMap = ConfigParser::generateMap($attributes, 'key', Attribute::class); - $typedAudienceIdMap = ConfigParser::generateMap($typedAudiences, 'id', Audience::class); - $this->_audienceIdMap = ConfigParser::generateMap($audiences, 'id', Audience::class); + $this->_eventKeyMap = ConfigParser::generateMap($this->events, 'key', Event::class); + $this->_attributeKeyMap = ConfigParser::generateMap($this->attributes, 'key', Attribute::class); + $typedAudienceIdMap = ConfigParser::generateMap($this->typedAudiences, 'id', Audience::class); + $this->_audienceIdMap = ConfigParser::generateMap($this->audiences, 'id', Audience::class); $this->_rollouts = ConfigParser::generateMap($rollouts, null, Rollout::class); $this->_featureFlags = ConfigParser::generateMap($featureFlags, null, FeatureFlag::class); @@ -449,6 +489,22 @@ public function getRevision() return $this->_revision; } + /** + * @return string Config environmentKey. + */ + public function getEnvironmentKey() + { + return $this->environmentKey; + } + + /** + * @return string Config sdkKey. + */ + public function getSdkKey() + { + return $this->sdkKey; + } + /** * @return array List of feature flags parsed from the datafile */ @@ -457,6 +513,38 @@ public function getFeatureFlags() return $this->_featureFlags; } + /** + * @return array List of attributes parsed from the datafile + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * @return array List of audiences parsed from the datafile + */ + public function getAudiences() + { + return $this->audiences; + } + + /** + * @return array List of events parsed from the datafile + */ + public function getEvents() + { + return $this->events; + } + + /** + * @return array List of typed audiences parsed from the datafile + */ + public function getTypedAudiences() + { + return $this->typedAudiences; + } + /** * @return array List of all experiments (including group experiments) * parsed from the datafile @@ -470,9 +558,12 @@ public function getAllExperiments() $rolloutExperimentIds[] = $experiment->getId(); } } - return array_filter(array_values($this->_experimentKeyMap), function ($experiment) use ($rolloutExperimentIds) { - return !in_array($experiment->getId(), $rolloutExperimentIds); - }); + return array_filter( + array_values($this->_experimentIdMap), + function ($experiment) use ($rolloutExperimentIds) { + return !in_array($experiment->getId(), $rolloutExperimentIds); + } + ); } /** diff --git a/src/Optimizely/Config/ProjectConfigInterface.php b/src/Optimizely/Config/ProjectConfigInterface.php index 53a6d6c2..e85be7cb 100644 --- a/src/Optimizely/Config/ProjectConfigInterface.php +++ b/src/Optimizely/Config/ProjectConfigInterface.php @@ -51,6 +51,37 @@ public function getBotFiltering(); */ public function getRevision(); + /** + * @return string String represnting environment key of the datafile. + */ + public function getEnvironmentKey(); + + /** + * @return string String representing sdkkey of the datafile. + */ + public function getSdkKey(); + + /** + * @return array List of attributes parsed from the datafile + */ + public function getAttributes(); + + /** + * @return array List of audiences parsed from the datafile + */ + public function getAudiences(); + + /** + * @return array List of events parsed from the datafile + */ + public function getEvents(); + + /** + * @return array List of typed audiences parsed from the datafile + */ + public function getTypedAudiences(); + + /** * @return array List of feature flags parsed from the datafile */ diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyAttribute.php b/src/Optimizely/OptimizelyConfig/OptimizelyAttribute.php new file mode 100644 index 00000000..d3eee9b9 --- /dev/null +++ b/src/Optimizely/OptimizelyConfig/OptimizelyAttribute.php @@ -0,0 +1,60 @@ +id = $id; + $this->key = $key; + } + + /** + * @return string attribute id. + */ + public function getId() + { + return $this->id; + } + + /** + * @return string attribute key. + */ + public function getKey() + { + return $this->key; + } + + /** + * @return string JSON representation of the object. + */ + public function jsonSerialize() + { + return get_object_vars($this); + } +} diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyAudience.php b/src/Optimizely/OptimizelyConfig/OptimizelyAudience.php new file mode 100644 index 00000000..e331871a --- /dev/null +++ b/src/Optimizely/OptimizelyConfig/OptimizelyAudience.php @@ -0,0 +1,75 @@ +id = $id; + $this->name = $name; + $this->conditions = $conditions; + } + + /** + * @return string audience id. + */ + public function getId() + { + return $this->id; + } + + /** + * @return string audience name. + */ + public function getName() + { + return $this->name; + } + + /** + * @return string audience conditions. + */ + public function getConditions() + { + return $this->conditions; + } + + /** + * @return string JSON representation of the object. + */ + public function jsonSerialize() + { + return get_object_vars($this); + } +} diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php index fdd5736c..a92209d7 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php @@ -1,6 +1,6 @@ environmentKey = $environmentKey; + $this->sdkKey = $sdkKey; $this->revision = $revision; $this->experimentsMap = $experimentsMap; $this->featuresMap = $featuresMap; + $this->attributes = $attributes; + $this->audiences = $audiences; + $this->events = $events; $this->datafile = $datafile; } + /** + * @return string Config environmentKey. + */ + public function getEnvironmentKey() + { + return $this->environmentKey; + } + + /** + * @return string Config sdkKey. + */ + public function getSdkKey() + { + return $this->sdkKey; + } + /** * @return string Config revision. */ @@ -83,6 +135,30 @@ public function getFeaturesMap() return $this->featuresMap; } + /** + * @return array Attributes as OptimizelyAttribute. + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * @return array Audiences as OptimizelyAudience. + */ + public function getAudiences() + { + return $this->audiences; + } + + /** + * @return array Events as OptimizelyEvent. + */ + public function getEvents() + { + return $this->events; + } + /** * @return string JSON representation of the object. */ diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php index eef1829a..e0efdc57 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php @@ -1,6 +1,6 @@ featureFlags = $projectConfig->getFeatureFlags(); $this->revision = $projectConfig->getRevision(); $this->datafile = $projectConfig->toDatafile(); + $this->environmentKey = $projectConfig->getEnvironmentKey(); + $this->sdkKey = $projectConfig->getSdkKey(); + $this->projectConfig = $projectConfig; $this->createLookupMaps(); } @@ -80,15 +93,98 @@ public function getConfig() { $experimentsMaps = $this->getExperimentsMaps(); $featuresMap = $this->getFeaturesMap($experimentsMaps[1]); + $attributes = $this->getConfigAttributes(); + $audiences = $this->getConfigAudiences(); + $events = $this->getConfigEvents(); return new OptimizelyConfig( $this->revision, $experimentsMaps[0], $featuresMap, - $this->datafile + $this->datafile, + $this->environmentKey, + $this->sdkKey, + $attributes, + $audiences, + $events ); } - + + /** + * Generates array of attributes as OptimizelyAttribute. + * + * @return array of OptimizelyAttributes. + */ + protected function getConfigAttributes() + { + $attributeArray = []; + $attributes = $this->projectConfig->getAttributes(); + foreach ($attributes as $attr) { + $optlyAttr = new OptimizelyAttribute( + $attr['id'], + $attr['key'] + ); + array_push($attributeArray, $optlyAttr); + } + return $attributeArray; + } + + + /** + * Generates array of events as OptimizelyEvents. + * + * @return array of OptimizelyEvents. + */ + protected function getConfigEvents() + { + $eventsArray = []; + $events = $this->projectConfig->getEvents(); + foreach ($events as $event) { + $optlyEvent = new OptimizelyEvent( + $event['id'], + $event['key'], + $event['experimentIds'] + ); + $eventsArray[] = $optlyEvent; + } + return $eventsArray; + } + + /** + * Generates array of audiences giving typed audiences high priority as OptimizelyAudience. + * + * @return array of OptimizelyEvents. + */ + protected function getConfigAudiences() + { + $finalAudiences = []; + $uniqueIdsMap = []; + $normalAudiences = $this->projectConfig->getAudiences(); + $typedAudiences = $this->projectConfig->getTypedAudiences(); + $audiencesArray = $typedAudiences; + foreach ($audiencesArray as $key => $typedAudience) { + $uniqueIdsMap[$typedAudience['id']] = $typedAudience['id']; + $audiencesArray[$key]['conditions'] = json_encode($typedAudience['conditions']); + } + foreach ($normalAudiences as $naudience) { + if (!array_key_exists($naudience['id'], $uniqueIdsMap)) { + array_push($audiencesArray, $naudience); + } + } + foreach ($audiencesArray as $audience) { + $id = $audience['id']; + if ($id != '$opt_dummy_audience') { + $optlyAudience = new OptimizelyAudience( + $id, + $audience['name'], + $audience['conditions'] + ); + array_push($finalAudiences, $optlyAudience); + } + } + return $finalAudiences; + } + /** * Generates lookup maps to avoid redundant iteration while creating OptimizelyConfig. */ @@ -103,7 +199,11 @@ protected function createLookupMaps() foreach ($feature->getExperimentIds() as $expId) { $this->experimentIdFeatureMap[$expId] = $feature; } - + $rolloutID = $feature->getRolloutId(); + $rollout = $this->projectConfig->getRolloutFromId($rolloutID); + foreach ($rollout->getExperiments() as $exp) { + $this->experimentIdFeatureMap[$exp->getId()] = $feature; + } # Populate featKeyOptlyVariableKeyVariableMap and featKeyOptlyVariableIdVariableMap $variablesKeyMap = []; $variablesIdMap = []; @@ -145,6 +245,7 @@ protected function getVariablesMap(Experiment $experiment, Variation $variation) } $featureFlag = $this->experimentIdFeatureMap[$experimentId]; + $featureKey = $featureFlag->getKey(); // Set default variables for variation. @@ -207,6 +308,80 @@ protected function getVariationsMap(Experiment $experiment) return $variationsMap; } + /** + * Converts array of audience conditions to serialized audiences. + * + * for examples: + * 1. Input: ["or", "1", "2"] + * Output: "us" OR "female" + * 2. Input: ["not", "1"] + * Output: "NOT "us" + * 3. Input: ["or", "1"] + * Output: "us" + * 4. Input: ["and", ["or", "1", ["and", "2", "3"]], ["and", "11", ["or", "12", "13"]]] + * Output: "("us" OR ("female" AND "adult")) AND ("fr" AND ("male" OR "kid"))" + * + * @param array audience conditions . + * + * @return string of experiment audience conditions. + */ + protected function getSerializedAudiences(array $audienceConditions) + { + $finalAudiences = ''; + if ($audienceConditions == null) { + return $finalAudiences; + } + $cond = ''; + foreach ($audienceConditions as $var) { + $subAudience = ''; + // Checks if item is list of conditions means if it is sub audience + if (is_array($var)) { + $subAudience = $this->getSerializedAudiences($var); + + $subAudience = '(' . $subAudience . ')'; + } elseif (in_array($var, array('and', 'or', 'not'), true)) { + $cond = strtoupper(strval($var)); + } else { + // Checks if item is audience id + $itemStr = strval($var); + $audience = $this->projectConfig->getAudience($itemStr); + $name = $audience == null ? $itemStr : $audience->getName(); + // if audience condition is "NOT" then add "NOT" at start. Otherwise check if there is already audience id in finalAudiences then append condition between finalAudiences and item + if ($finalAudiences !== '' || $cond == "NOT") { + if ($finalAudiences !== '') { + $finalAudiences = $finalAudiences . ' '; + } else { + $finalAudiences = $finalAudiences; + } + if ($cond == '') { + $cond = 'OR'; + } + $finalAudiences = $finalAudiences . $cond . ' ' . '"' . $name . '"'; + } else { + $finalAudiences = '"' . $name . '"'; + } + } + // Checks if sub audience is empty or not + if (strval($subAudience !== '')) { + if ($finalAudiences !== '' || $cond == "NOT") { + if ($finalAudiences !== '') { + $finalAudiences = $finalAudiences . ' '; + } else { + $finalAudiences = $finalAudiences; + } + if ($cond == '') { + $cond = 'OR'; + } + $finalAudiences = $finalAudiences . $cond . ' ' . $subAudience; + } else { + $finalAudiences = $finalAudiences . $subAudience; + } + } + } + return $finalAudiences; + } + + /** * Generates OptimizelyExperiment Key and ID Maps. * Returns an array with @@ -223,11 +398,16 @@ protected function getExperimentsMaps() foreach ($this->experiments as $exp) { $expId = $exp->getId(); $expKey = $exp->getKey(); - + $audiences = ''; + if ($exp->getAudienceConditions() != null) { + $audienceConditions = $exp->getAudienceConditions(); + $audiences = $this->getSerializedAudiences($audienceConditions); + } $optExp = new OptimizelyExperiment( $expId, $expKey, - $this->getVariationsMap($exp) + $this->getVariationsMap($exp), + $audiences ); $experimentsKeyMap[$expKey] = $optExp; @@ -237,6 +417,39 @@ protected function getExperimentsMaps() return [$experimentsKeyMap, $experimentsIdMap]; } + /** + * Generates array of delivery rules for optimizelyFeature. + * + * @param string feature rollout id. + * + * @return array of optimizelyExperiments as delivery rules . + */ + protected function getDeliveryRules($rollout_id) + { + $deliveryRules = []; + $rollout = $this->projectConfig->getRolloutFromId($rollout_id); + $experiments = $rollout->getExperiments(); + foreach ($experiments as $exp) { + $expId = $exp->getId(); + $expKey = $exp->getKey(); + $audiences = ''; + if ($exp->getAudienceConditions() != null) { + $audienceConditions = $exp->getAudienceConditions(); + $audiences = $this->getSerializedAudiences($audienceConditions); + } + $optExp = new OptimizelyExperiment( + $expId, + $expKey, + $this->getVariationsMap($exp), + $audiences + ); + array_push($deliveryRules, $optExp); + } + + return $deliveryRules; + } + + /** * Generates Features map for the project config. * @@ -251,10 +464,15 @@ protected function getFeaturesMap(array $experimentsIdMap) foreach ($this->featureFlags as $feature) { $featureKey = $feature->getKey(); $experimentsMap = []; - + $experimentRules = []; + $deliveryRules = []; + if ($feature->getRolloutId() != null) { + $deliveryRules = $this->getDeliveryRules($feature->getRolloutId()); + } foreach ($feature->getExperimentIds() as $expId) { $optExp = $experimentsIdMap[$expId]; $experimentsMap[$optExp->getKey()] = $optExp; + array_push($experimentRules, $optExp); } $variablesMap = $this->featKeyOptlyVariableKeyVariableMap[$featureKey]; @@ -263,7 +481,9 @@ protected function getFeaturesMap(array $experimentsIdMap) $feature->getId(), $featureKey, $experimentsMap, - $variablesMap + $variablesMap, + $experimentRules, + $deliveryRules ); $featuresMap[$featureKey] = $optFeature; diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php b/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php new file mode 100644 index 00000000..be8400d3 --- /dev/null +++ b/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php @@ -0,0 +1,75 @@ +id = $id; + $this->key = $key; + $this->experimentIds = $experimentIds; + } + + /** + * @return string event ID. + */ + public function getId() + { + return $this->id; + } + + /** + * @return string event Key. + */ + public function getKey() + { + return $this->key; + } + + /** + * @return string event conditions. + */ + public function getExperimentIds() + { + return $this->experimentIds; + } + + /** + * @return string JSON representation of the object. + */ + public function jsonSerialize() + { + return get_object_vars($this); + } +} diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyExperiment.php b/src/Optimizely/OptimizelyConfig/OptimizelyExperiment.php index ed7b32b5..71bd9959 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyExperiment.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyExperiment.php @@ -1,6 +1,6 @@ id = $id; $this->key = $key; + $this->audiences = $audiences; $this->variationsMap = $variationsMap; } @@ -58,6 +64,14 @@ public function getKey() return $this->key; } + /** + * @return string Experiment audiences. + */ + public function getExperimentAudiences() + { + return $this->audiences; + } + /** * @return array Map of Variation Keys to OptimizelyVariations. */ diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php index 63f1afec..bd674b92 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php @@ -1,6 +1,6 @@ associative array + */ + private $experimentRules; + + /** + * Map of rollout Experiments Keys to OptimizelyExperiments. + * + * @var associative array + */ + private $deliveryRules; + /** * Map of Experiment Keys to OptimizelyExperiments. * @@ -42,10 +56,12 @@ class OptimizelyFeature implements \JsonSerializable */ private $variablesMap; - public function __construct($id, $key, array $experimentsMap, array $variablesMap) + public function __construct($id, $key, array $experimentsMap, array $variablesMap, array $experimentRules, array $deliveryRules) { $this->id = $id; $this->key = $key; + $this->experimentRules = $experimentRules; + $this->deliveryRules = $deliveryRules; $this->experimentsMap = $experimentsMap; $this->variablesMap = $variablesMap; } @@ -66,6 +82,22 @@ public function getKey() return $this->key; } + /** + * @return array array of feature Experiments as OptimizelyExperiments. + */ + public function getExperimentRules() + { + return $this->experimentRules; + } + + /** + * @return array array of Rollout Experiments of feature as OptimizelyExperiments. + */ + public function getDeliveryRules() + { + return $this->deliveryRules; + } + /** * @return array Map of Experiment Keys to OptimizelyExperiments. */ diff --git a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php index 0a1d5ad8..eb55c5fe 100644 --- a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php +++ b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php @@ -1,6 +1,6 @@ featExpVariationMap); - - + new OptimizelyExperiment("17279300791", "feat_experiment", $this->featExpVariationMap, ''); + + // creating optimizely Experiment for delivery rules + $boolDeliveryFeatVariable = new OptimizelyVariable('17252790456', 'boolean_var', 'boolean', 'false'); + $intDeliveryFeatVariable = new OptimizelyVariable('17258820367', 'integer_var', 'integer', 1); + $doubleDeliveryFeatVariable = new OptimizelyVariable('17260550714', 'double_var', 'double', 0.5); + $strDeliveryFeatVariable = new OptimizelyVariable('17290540010', 'string_var', 'string', 'i am default value'); + $jsonDeliveryFeatVariable = new OptimizelyVariable('17260550458', 'json_var', 'json', "{\"text\": \"default value\"}"); + + $this->deliveryDefaultVariableKeyMap = []; + $this->deliveryDefaultVariableKeyMap['boolean_var'] = $boolDeliveryFeatVariable; + $this->deliveryDefaultVariableKeyMap['integer_var'] = $intDeliveryFeatVariable; + $this->deliveryDefaultVariableKeyMap['double_var'] = $doubleDeliveryFeatVariable; + $this->deliveryDefaultVariableKeyMap['string_var'] = $strDeliveryFeatVariable; + $this->deliveryDefaultVariableKeyMap['json_var'] = $jsonDeliveryFeatVariable; + $this->deliveryExpVariationMap = []; + $this->deliveryExpVariationMap['17285550838'] = + new OptimizelyVariation('17285550838', '17285550838', true, $this->deliveryDefaultVariableKeyMap); + + $del_Experiment = + new OptimizelyExperiment("17268110732", "17268110732", $this->deliveryExpVariationMap, ''); // create feature $experimentsMap = ['feat_experiment' => $featExperiment]; + $experiment_rules = [$featExperiment]; + $deliver_rules = [$del_Experiment]; $this->feature = new OptimizelyFeature( '17266500726', 'test_feature', $experimentsMap, - $this->expectedDefaultVariableKeyMap + $this->expectedDefaultVariableKeyMap, + $experiment_rules, + $deliver_rules ); // create ab experiment and variations @@ -100,7 +127,7 @@ public function setUp() $variationsMap['variation_a'] = $variationA; $variationsMap['variation_b'] = $variationB; - $abExperiment = new OptimizelyExperiment('17301270474', 'ab_experiment', $variationsMap); + $abExperiment = new OptimizelyExperiment('17301270474', 'ab_experiment', $variationsMap, ''); // create group_ab_experiment and variations $variationA = new OptimizelyVariation('17287500312', 'variation_a', null, []); @@ -110,7 +137,7 @@ public function setUp() $variationsMap['variation_b'] = $variationB; $groupExperiment = - new OptimizelyExperiment('17258450439', 'group_ab_experiment', $variationsMap); + new OptimizelyExperiment('17258450439', 'group_ab_experiment', $variationsMap, ''); // create experiment key map $this->expectedExpKeyMap = []; @@ -172,10 +199,49 @@ public function testGetVariationsMap() $getVariationsMap = self::getMethod("getVariationsMap"); $response = $getVariationsMap->invokeArgs($this->optConfigService, array($featExp)); - $this->assertEquals($this->featExpVariationMap, $response); } + public function testGetOptimizelyConfigWithDuplicateExperimentKeys() + { + $this->datafile = DATAFILE_FOR_DUPLICATE_EXP_KEYS; + $this->projectConfig = new DatafileProjectConfig( + $this->datafile, + new NoOpLogger(), + new NoOpErrorHandler() + ); + $this->optConfigService = new OptimizelyConfigService($this->projectConfig); + $optimizelyConfig = $this->optConfigService->getConfig(); + $this->assertEquals(Count($optimizelyConfig->getExperimentsMap()), 1); + $experimentMapFlag1 = $optimizelyConfig->getFeaturesMap()['flag1']->getExperimentsMap(); // 9300000007569 + $experimentMapFlag2 = $optimizelyConfig->getFeaturesMap()['flag2']->getExperimentsMap(); // 9300000007573 + $this->assertEquals($experimentMapFlag1['targeted_delivery']->getId(), '9300000007569'); + $this->assertEquals($experimentMapFlag2['targeted_delivery']->getId(), '9300000007573'); + } + + public function testGetOptimizelyConfigWithDuplicateRuleKeys() + { + $this->datafile = DATAFILE_FOR_DUPLICATE_RUL_KEYS; + $this->projectConfig = new DatafileProjectConfig( + $this->datafile, + new NoOpLogger(), + new NoOpErrorHandler() + ); + $this->optConfigService = new OptimizelyConfigService($this->projectConfig); + + $optimizelyConfig = $this->optConfigService->getConfig(); + $this->assertEquals(Count($optimizelyConfig->getExperimentsMap()), 0); + $rolloutFlag1 = $optimizelyConfig->getFeaturesMap()["flag_1"]->getDeliveryRules()[0]; // 9300000004977 + $rolloutFlag2 = $optimizelyConfig->getFeaturesMap()["flag_2"]->getDeliveryRules()[0]; // 9300000004979 + $rolloutFlag3 = $optimizelyConfig->getFeaturesMap()["flag_3"]->getDeliveryRules()[0]; // 9300000004981 + $this->assertEquals($rolloutFlag1->getId(), "9300000004977"); + $this->assertEquals($rolloutFlag1->getKey(), "targeted_delivery"); + $this->assertEquals($rolloutFlag2->getId(), "9300000004979"); + $this->assertEquals($rolloutFlag2->getKey(), "targeted_delivery"); + $this->assertEquals($rolloutFlag3->getId(), "9300000004981"); + $this->assertEquals($rolloutFlag3->getKey(), "targeted_delivery"); + } + public function testGetExperimentsMaps() { $getExperimentsMap = self::getMethod("getExperimentsMaps"); @@ -197,6 +263,112 @@ public function testGetFeaturesMap() ); $this->assertEquals(['test_feature' => $this->feature], $response); + foreach ($response as $feature) { + $this->assertInstanceof(OptimizelyFeature::class, $feature); + $experiment_rules = $feature->getExperimentRules(); + $deliver_rules = $feature->getDeliveryRules(); + if (!empty($experiment_rules)) { + foreach ($experiment_rules as $exp) { + $this->assertInstanceof(OptimizelyExperiment::class, $exp); + } + } + if (!empty($deliver_rules)) { + foreach ($deliver_rules as $del) { + $this->assertInstanceof(OptimizelyExperiment::class, $del); + } + } + } + } + + public function testgetExperimentAudiences() + { + $audienceConditions = [ + array("or", "3468206642"), + array('or', '3468206642', '3988293899'), + array('or', '3468206642', '3988293899', '3988293898'), + array("not", "3468206642"), + array('and', '3468206642', '3988293899'), + array("and", "3468206642"), + array('3468206642', '3988293899'), + array("3468206642"), + array('and', array('or', '3468206642', '3988293899'), '3988293898'), + array('and', 'and'), + array(), + array('not', array('and', '3468206642', '3988293899')), + array( + "and", array("or", "3468206642", array( + 'and', '3468206642', '3988293899' + )), + array( + "and", "3468206642", + array('or', '3468206642', '3988293899') + ) + ) + ]; + + $expectedAudienceOutputs = [ + '"' . "exactString" . '"', + '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"', + '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummySubstringString' . '"', + 'NOT' . ' ' . '"' . "exactString" . '"', + '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '"' . '$$dummyExists' . '"', + '"' . "exactString" . '"', + '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"', + '"' . "exactString" . '"', + '(' . '"' . 'exactString' . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ')' + . ' ' . 'AND' . ' ' . '"' . '$$dummySubstringString' . '"', + '', + '', + 'NOT' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '"' . '$$dummyExists' . '"' . ')', + + '(' . '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . + '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')' + . ' ' . 'AND' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '(' . + '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')' + ]; + + for ($testNo = 0; $testNo < count($audienceConditions); $testNo++) { + $getExperimentAudiences = self::getMethod("getSerializedAudiences"); + $response = $getExperimentAudiences->invokeArgs( + $this->optConfigService, + array($audienceConditions[$testNo]) + ); + $this->assertEquals($expectedAudienceOutputs[$testNo], $response); + } + } + + public function testgetConfigAttributes() + { + $getConfigAttributes = self::getMethod("getConfigAttributes"); + $response = $getConfigAttributes->invokeArgs($this->optConfigService, array()); + if (!empty($response)) { + foreach ($response as $attr) { + $this->assertInstanceof(OptimizelyAttribute::class, $attr); + } + } + } + + public function testgetConfigAudiences() + { + $getConfigAudiences = self::getMethod("getConfigAudiences"); + $response = $getConfigAudiences->invokeArgs($this->optConfigService, array()); + if (!empty($response)) { + foreach ($response as $attr) { + $this->assertInstanceof(OptimizelyAudience::class, $attr); + } + } + } + + + public function testgetConfigEvents() + { + $getConfigEvents = self::getMethod("getConfigEvents"); + $response = $getConfigEvents->invokeArgs($this->optConfigService, array()); + if (!empty($response)) { + foreach ($response as $event) { + $this->assertInstanceof(OptimizelyEvent::class, $event); + } + } } public function testGetConfig() @@ -220,11 +392,14 @@ public function testJsonEncodeofOptimizelyConfig() $response = $this->optConfigService->getConfig(); $expectedJSON = '{ + "environmentKey":"", + "sdkKey":"", "revision": "16", "experimentsMap": { "ab_experiment": { "id": "17301270474", "key": "ab_experiment", + "audiences":"", "variationsMap": { "variation_a": { "id": "17277380360", @@ -245,6 +420,7 @@ public function testJsonEncodeofOptimizelyConfig() "feat_experiment": { "id": "17279300791", "key": "feat_experiment", + "audiences":"", "variationsMap": { "variation_a": { "id": "17289540366", @@ -325,6 +501,7 @@ public function testJsonEncodeofOptimizelyConfig() "group_ab_experiment": { "id": "17258450439", "key": "group_ab_experiment", + "audiences":"", "variationsMap": { "variation_a": { "id": "17287500312", @@ -347,10 +524,14 @@ public function testJsonEncodeofOptimizelyConfig() "test_feature": { "id": "17266500726", "key": "test_feature", + "experimentRules":[{"id":"17279300791","key":"feat_experiment","audiences":"","variationsMap":{"variation_a":{"id":"17289540366","key":"variation_a","featureEnabled":true,"variablesMap":{"boolean_var":{"id":"17252790456","key":"boolean_var","type":"boolean","value":"true"},"integer_var":{"id":"17258820367","key":"integer_var","type":"integer","value":"5"},"double_var":{"id":"17260550714","key":"double_var","type":"double","value":"5.5"},"string_var":{"id":"17290540010","key":"string_var","type":"string","value":"i am variable value"},"json_var":{"id":"17260550458","key":"json_var","type":"json","value":"{\"text\": \"variable value\"}"}}},"variation_b":{"id":"17304990114","key":"variation_b","featureEnabled":false,"variablesMap":{"boolean_var":{"id":"17252790456","key":"boolean_var","type":"boolean","value":"false"},"integer_var":{"id":"17258820367","key":"integer_var","type":"integer","value":"1"},"double_var":{"id":"17260550714","key":"double_var","type":"double","value":"0.5"},"string_var":{"id":"17290540010","key":"string_var","type":"string","value":"i am default value"},"json_var":{"id":"17260550458","key":"json_var","type":"json","value":"{\"text\": \"default value\"}"}}}}}], + + "deliveryRules":[{"id":"17268110732","key":"17268110732","audiences":"","variationsMap":{"17285550838":{"id":"17285550838","key":"17285550838","featureEnabled":true,"variablesMap":{"boolean_var":{"id":"17252790456","key":"boolean_var","type":"boolean","value":"false"},"integer_var":{"id":"17258820367","key":"integer_var","type":"integer","value":"1"},"double_var":{"id":"17260550714","key":"double_var","type":"double","value":"0.5"},"string_var":{"id":"17290540010","key":"string_var","type":"string","value":"i am default value"},"json_var":{"id":"17260550458","key":"json_var","type":"json","value":"{\"text\": \"default value\"}"}}}}}], "experimentsMap": { "feat_experiment": { "id": "17279300791", "key": "feat_experiment", + "audiences":"", "variationsMap": { "variation_a": { "id": "17289540366", @@ -466,8 +647,12 @@ public function testJsonEncodeofOptimizelyConfig() }'; $optimizelyConfig = json_decode($expectedJSON, true); - $optimizelyConfig['datafile'] = DATAFILE_FOR_OPTIMIZELY_CONFIG; - + $optimizelyConfig["attributes"] = [["id" => "111094", "key" => "test_attribute"]]; + $json_encoded = '[{"id":"3468206642","name":"exactString","conditions":"[\"and\", [\"or\", [\"or\", {\"name\": \"house\", \"type\": \"custom_attribute\", \"value\": \"Gryffindor\"}]]]"},{"id":"3988293898","name":"$$dummySubstringString","conditions":"{ \"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\" }"},{"id":"3988293899","name":"$$dummyExists","conditions":"{ \"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\" }"}]'; + $converted_audiences = json_decode($json_encoded, true); + $optimizelyConfig["audiences"] = $converted_audiences; + $optimizelyConfig["events"] = [["id" => "111095", "key" => "test_event", "experimentIds" => ["111127"]]]; + $optimizelyConfig['datafile'] = DATAFILE_FOR_OPTIMIZELY_CONFIG; $this->assertEquals(json_encode($optimizelyConfig), json_encode($response)); } diff --git a/tests/OptimizelyConfigTests/OptimizelyEntitiesTest.php b/tests/OptimizelyConfigTests/OptimizelyEntitiesTest.php index 3551af3f..995d2364 100644 --- a/tests/OptimizelyConfigTests/OptimizelyEntitiesTest.php +++ b/tests/OptimizelyConfigTests/OptimizelyEntitiesTest.php @@ -37,11 +37,18 @@ public function testOptimizelyConfigEntity() $this->assertEquals("20", $optConfig->getRevision()); $this->assertEquals(["a" => "apple"], $optConfig->getExperimentsMap()); $this->assertEquals(["o" => "orange"], $optConfig->getFeaturesMap()); + $this->assertEquals("", $optConfig->getEnvironmentKey()); + $this->assertEquals("", $optConfig->getSdkKey()); $expectedJson = '{ + "environmentKey":"", + "sdkKey":"", "revision": "20", "experimentsMap" : {"a": "apple"}, "featuresMap": {"o": "orange"}, + "attributes":[], + "audiences":[], + "events":[], "datafile": null }'; @@ -55,16 +62,19 @@ public function testOptimizelyExperimentEntity() $optExp = new OptimizelyExperiment( "id", "key", - ["a" => "apple"] + ["a" => "apple"], + '' ); $this->assertEquals("id", $optExp->getId()); $this->assertEquals("key", $optExp->getKey()); $this->assertEquals(["a" => "apple"], $optExp->getVariationsMap()); + $this->assertEquals('', $optExp->getExperimentAudiences()); $expectedJson = '{ "id": "id", "key" : "key", + "audiences":"", "variationsMap": {"a": "apple"} }'; @@ -79,17 +89,23 @@ public function testOptimizelyFeatureEntity() "id", "key", ["a" => "apple"], - ["o" => "orange"] + ["o" => "orange"], + [], + [] ); $this->assertEquals("id", $optFeature->getId()); $this->assertEquals("key", $optFeature->getKey()); $this->assertEquals(["a" => "apple"], $optFeature->getExperimentsMap()); $this->assertEquals(["o" => "orange"], $optFeature->getVariablesMap()); + $this->assertEquals([], $optFeature->getExperimentRules()); + $this->assertEquals([], $optFeature->getDeliveryRules()); $expectedJson = '{ "id": "id", "key" : "key", + "experimentRules":[], + "deliveryRules":[], "experimentsMap": {"a": "apple"}, "variablesMap": {"o": "orange"} }'; diff --git a/tests/TestData.php b/tests/TestData.php index 0d668702..10c9ed83 100644 --- a/tests/TestData.php +++ b/tests/TestData.php @@ -145,93 +145,93 @@ "id": "122230", "forcedVariations": { - }, - "trafficAllocation": [ - { - "entityId": "122231", - "endOfRange": 2500 - }, - { - "entityId": "122232", - "endOfRange": 5000 - }, - { - "entityId": "122233", - "endOfRange": 7500 }, - { - "entityId": "122234", - "endOfRange": 10000 - } - ], - "variations": [ - { - "id": "122231", - "key": "Fred", - "variables": [ + "trafficAllocation": [ { - "id": "155560", - "value": "F" + "entityId": "122231", + "endOfRange": 2500 }, { - "id": "155561", - "value": "red" - } - ], - "featureEnabled": true - }, - { - "id": "122232", - "key": "Feorge", - "variables": [ + "entityId": "122232", + "endOfRange": 5000 + }, { - "id": "155560", - "value": "F" + "entityId": "122233", + "endOfRange": 7500 }, { - "id": "155561", - "value": "eorge" + "entityId": "122234", + "endOfRange": 10000 } - ], - "featureEnabled": true - }, - { - "id": "122233", - "key": "Gred", - "variables": [ + ], + "variations": [ { - "id": "155560", - "value": "G" + "id": "122231", + "key": "Fred", + "variables": [ + { + "id": "155560", + "value": "F" + }, + { + "id": "155561", + "value": "red" + } + ], + "featureEnabled": true }, { - "id": "155561", - "value": "red" - } - ], - "featureEnabled": true - }, - { - "id": "122234", - "key": "George", - "variables": [ + "id": "122232", + "key": "Feorge", + "variables": [ + { + "id": "155560", + "value": "F" + }, + { + "id": "155561", + "value": "eorge" + } + ], + "featureEnabled": true + }, { - "id": "155560", - "value": "G" + "id": "122233", + "key": "Gred", + "variables": [ + { + "id": "155560", + "value": "G" + }, + { + "id": "155561", + "value": "red" + } + ], + "featureEnabled": true }, { - "id": "155561", - "value": "eorge" + "id": "122234", + "key": "George", + "variables": [ + { + "id": "155560", + "value": "G" + }, + { + "id": "155561", + "value": "eorge" + } + ], + "featureEnabled": true } - ], - "featureEnabled": true - } - ] - }, - { - "key": "test_experiment_with_feature_rollout", - "status": "Running", - "layerId": "5", - "audienceIds": [ + ] + }, + { + "key": "test_experiment_with_feature_rollout", + "status": "Running", + "layerId": "5", + "audienceIds": [ ], "id": "122235", @@ -1575,7 +1575,23 @@ "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", "id": "$opt_dummy_audience", "name": "Optimizely-Generated Audience for Backwards Compatibility" + }, + { + "id": "3468206642", + "name": "exactString", + "conditions": "[\"and\", [\"or\", [\"or\", {\"name\": \"house\", \"type\": \"custom_attribute\", \"value\": \"Gryffindor\"}]]]" + }, + { + "id": "3988293898", + "name": "$$dummySubstringString", + "conditions": "{ \"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\" }" + }, + { + "id": "3988293899", + "name": "$$dummyExists", + "conditions": "{ \"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\" }" } + ], "groups": [ { @@ -1693,15 +1709,318 @@ } ], "attributes": [ - - ], - "botFiltering": false, - "accountId": "8272261422", - "events": [ - + {"key": "test_attribute", "id": "111094"} + ], + "botFiltering": false, + "accountId": "8272261422", + "events": [ + {"key": "test_event", "experimentIds": ["111127"], "id": "111095"} + ], + "revision": "16" + }' +); + +define( + 'DATAFILE_FOR_DUPLICATE_EXP_KEYS', + '{ + "version": "4", + "rollouts": [], + "typedAudiences": [ + { + "id": "20415611520", + "conditions": [ + "and", + [ + "or", + [ + "or", + { + "value": true, + "type": "custom_attribute", + "name": "hiddenLiveEnabled", + "match": "exact" + } + ] + ] + ], + "name": "test1" + }, + { + "id": "20406066925", + "conditions": [ + "and", + [ + "or", + [ + "or", + { + "value": false, + "type": "custom_attribute", + "name": "hiddenLiveEnabled", + "match": "exact" + } + ] + ] + ], + "name": "test2" + } + ], + "anonymizeIP": true, + "projectId": "20430981610", + "variables": [], + "featureFlags": [ + { + "experimentIds": ["9300000007569"], + "rolloutId": "", + "variables": [], + "id": "3045", + "key": "flag1" + }, + { + "experimentIds": ["9300000007573"], + "rolloutId": "", + "variables": [], + "id": "3046", + "key": "flag2" + } ], - "revision": "16" - }' + "experiments": [ + { + "status": "Running", + "audienceConditions": ["or", "20415611520"], + "audienceIds": ["20415611520"], + "variations": [ + { + "variables": [], + "id": "8045", + "key": "variation1", + "featureEnabled": true + } + ], + "forcedVariations": {}, + "key": "targeted_delivery", + "layerId": "9300000007569", + "trafficAllocation": [{ "entityId": "8045", "endOfRange": 10000 }], + "id": "9300000007569" + }, + { + "status": "Running", + "audienceConditions": ["or", "20406066925"], + "audienceIds": ["20406066925"], + "variations": [ + { + "variables": [], + "id": "8048", + "key": "variation2", + "featureEnabled": true + } + ], + "forcedVariations": {}, + "key": "targeted_delivery", + "layerId": "9300000007573", + "trafficAllocation": [{ "entityId": "8048", "endOfRange": 10000 }], + "id": "9300000007573" + } + ], + "audiences": [ + { + "id": "20415611520", + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", + "name": "test1" + }, + { + "id": "20406066925", + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", + "name": "test2" + }, + { + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", + "id": "$opt_dummy_audience", + "name": "Optimizely-Generated Audience for Backwards Compatibility" + } + ], + "groups": [], + "attributes": [{ "id": "20408641883", "key": "hiddenLiveEnabled" }], + "botFiltering": false, + "accountId": "17882702980", + "events": [], + "revision": "25", + "sendFlagDecisions": true + }' +); + +define( + 'DATAFILE_FOR_DUPLICATE_RUL_KEYS', + '{ + "version": "4", + "rollouts": [ + { + "experiments": [ + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5452", + "key": "on", + "featureEnabled": true + } + ], + "forcedVariations": {}, + "key": "targeted_delivery", + "layerId": "9300000004981", + "trafficAllocation": [{ "entityId": "5452", "endOfRange": 10000 }], + "id": "9300000004981" + }, + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5451", + "key": "off", + "featureEnabled": false + } + ], + "forcedVariations": {}, + "key": "default-rollout-2029-20301771717", + "layerId": "default-layer-rollout-2029-20301771717", + "trafficAllocation": [{ "entityId": "5451", "endOfRange": 10000 }], + "id": "default-rollout-2029-20301771717" + } + ], + "id": "rollout-2029-20301771717" + }, + { + "experiments": [ + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5450", + "key": "on", + "featureEnabled": true + } + ], + "forcedVariations": {}, + "key": "targeted_delivery", + "layerId": "9300000004979", + "trafficAllocation": [{ "entityId": "5450", "endOfRange": 10000 }], + "id": "9300000004979" + }, + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5449", + "key": "off", + "featureEnabled": false + } + ], + "forcedVariations": {}, + "key": "default-rollout-2028-20301771717", + "layerId": "default-layer-rollout-2028-20301771717", + "trafficAllocation": [{ "entityId": "5449", "endOfRange": 10000 }], + "id": "default-rollout-2028-20301771717" + } + ], + "id": "rollout-2028-20301771717" + }, + { + "experiments": [ + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5448", + "key": "on", + "featureEnabled": true + } + ], + "forcedVariations": {}, + "key": "targeted_delivery", + "layerId": "9300000004977", + "trafficAllocation": [{ "entityId": "5448", "endOfRange": 10000 }], + "id": "9300000004977" + }, + { + "status": "Running", + "audienceConditions": [], + "audienceIds": [], + "variations": [ + { + "variables": [], + "id": "5447", + "key": "off", + "featureEnabled": false + } + ], + "forcedVariations": {}, + "key": "default-rollout-2027-20301771717", + "layerId": "default-layer-rollout-2027-20301771717", + "trafficAllocation": [{ "entityId": "5447", "endOfRange": 10000 }], + "id": "default-rollout-2027-20301771717" + } + ], + "id": "rollout-2027-20301771717" + } + ], + "typedAudiences": [], + "anonymizeIP": true, + "projectId": "20286295225", + "variables": [], + "featureFlags": [ + { + "experimentIds": [], + "rolloutId": "rollout-2029-20301771717", + "variables": [], + "id": "2029", + "key": "flag_3" + }, + { + "experimentIds": [], + "rolloutId": "rollout-2028-20301771717", + "variables": [], + "id": "2028", + "key": "flag_2" + }, + { + "experimentIds": [], + "rolloutId": "rollout-2027-20301771717", + "variables": [], + "id": "2027", + "key": "flag_1" + } + ], + "experiments": [], + "audiences": [ + { + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", + "id": "$opt_dummy_audience", + "name": "Optimizely-Generated Audience for Backwards Compatibility" + } + ], + "groups": [], + "attributes": [], + "botFiltering": false, + "accountId": "19947277778", + "events": [], + "revision": "11", + "sendFlagDecisions": true + }' ); /** From c56a186bfc7d7e30e0841f1fabaf2ac88d1e0c35 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Sun, 22 Aug 2021 06:00:05 +0500 Subject: [PATCH 2/9] removed decoder package --- tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php index eb55c5fe..667add8c 100644 --- a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php +++ b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php @@ -30,8 +30,6 @@ use Optimizely\OptimizelyConfig\OptimizelyVariable; use Optimizely\OptimizelyConfig\OptimizelyVariation; -use function GuzzleHttp\json_decode; - class OptimizelyConfigServiceTest extends \PHPUnit_Framework_TestCase { From 4176c4b9392a0727b84d71c8b334d81e2e80abab Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Mon, 23 Aug 2021 10:52:45 +0500 Subject: [PATCH 3/9] deprecation warning added --- src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php index e0efdc57..ba95344b 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php @@ -99,6 +99,10 @@ public function getConfig() return new OptimizelyConfig( $this->revision, + # This experimentsMap is for experiments of legacy projects only. + # For flag projects, experiment keys are not guaranteed to be unique + # across multiple flags, so this map may not include all experiments + # when keys conflict. Use experimentRules and deliveryRules instead. $experimentsMaps[0], $featuresMap, $this->datafile, @@ -480,6 +484,7 @@ protected function getFeaturesMap(array $experimentsIdMap) $optFeature = new OptimizelyFeature( $feature->getId(), $featureKey, + # This experimentsMap is deprecated. Use experimentRules and deliveryRules instead. $experimentsMap, $variablesMap, $experimentRules, From b67e2d12fa039077dde96034becca3d300a95d41 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Mon, 23 Aug 2021 20:07:49 +0500 Subject: [PATCH 4/9] typo corrected --- src/Optimizely/Config/ProjectConfigInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Optimizely/Config/ProjectConfigInterface.php b/src/Optimizely/Config/ProjectConfigInterface.php index e85be7cb..887e706c 100644 --- a/src/Optimizely/Config/ProjectConfigInterface.php +++ b/src/Optimizely/Config/ProjectConfigInterface.php @@ -52,7 +52,7 @@ public function getBotFiltering(); public function getRevision(); /** - * @return string String represnting environment key of the datafile. + * @return string String representing environment key of the datafile. */ public function getEnvironmentKey(); From 563003bcc9f8404d1b4afaa1f6a83976e5305962 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Tue, 24 Aug 2021 18:16:53 +0500 Subject: [PATCH 5/9] comments addressed --- .../Config/DatafileProjectConfig.php | 19 +++--- .../OptimizelyConfig/OptimizelyConfig.php | 5 ++ .../OptimizelyConfigService.php | 61 +++++++++---------- .../OptimizelyConfig/OptimizelyEvent.php | 2 +- .../OptimizelyConfig/OptimizelyFeature.php | 8 +-- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/Optimizely/Config/DatafileProjectConfig.php b/src/Optimizely/Config/DatafileProjectConfig.php index 42d16dfa..a5e47371 100644 --- a/src/Optimizely/Config/DatafileProjectConfig.php +++ b/src/Optimizely/Config/DatafileProjectConfig.php @@ -206,7 +206,7 @@ class DatafileProjectConfig implements ProjectConfigInterface /** * list of Typed Audiences that will be parsed from the datafile * - * @var [typed_audience] + * @var [Audience] */ private $typedAudiences; @@ -270,10 +270,6 @@ public function __construct($datafile, $logger, $errorHandler) $this->_accountId = $config['accountId']; $this->_projectId = $config['projectId']; - $this->attributes = isset($config['attributes']) ? $config['attributes'] : []; - $this->audiences = isset($config['audiences']) ? $config['audiences'] : []; - $this->events = $config['events'] ?: []; - $this->typedAudiences = isset($config['typedAudiences']) ? $config['typedAudiences'] : []; $this->_anonymizeIP = isset($config['anonymizeIP']) ? $config['anonymizeIP'] : false; $this->_botFiltering = isset($config['botFiltering']) ? $config['botFiltering'] : null; $this->_revision = $config['revision']; @@ -281,6 +277,10 @@ public function __construct($datafile, $logger, $errorHandler) $groups = $config['groups'] ?: []; $experiments = $config['experiments'] ?: []; + $this->attributes = isset($config['attributes']) ? $config['attributes'] : []; + $this->audiences = isset($config['audiences']) ? $config['audiences'] : []; + $this->events = isset($config['events']) ? $config['events'] : []; + $this->typedAudiences = isset($config['typedAudiences']) ? $config['typedAudiences'] : []; $rollouts = isset($config['rollouts']) ? $config['rollouts'] : []; $featureFlags = isset($config['featureFlags']) ? $config['featureFlags']: []; @@ -558,12 +558,9 @@ public function getAllExperiments() $rolloutExperimentIds[] = $experiment->getId(); } } - return array_filter( - array_values($this->_experimentIdMap), - function ($experiment) use ($rolloutExperimentIds) { - return !in_array($experiment->getId(), $rolloutExperimentIds); - } - ); + return array_filter(array_values($this->_experimentIdMap), function ($experiment) use ($rolloutExperimentIds) { + return !in_array($experiment->getId(), $rolloutExperimentIds); + }); } /** diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php index a92209d7..1ac14244 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php @@ -35,6 +35,10 @@ class OptimizelyConfig implements \JsonSerializable /** * Map of Experiment Keys to OptimizelyExperiments. + * This experimentsMap is for experiments of legacy projects only. + * For flag projects, experiment keys are not guaranteed to be unique + * across multiple flags, so this map may not include all experiments + * when keys conflict. Use experimentRules and deliveryRules instead. * * @var associative array */ @@ -124,6 +128,7 @@ public function getDatafile() */ public function getExperimentsMap() { + # This experimentsMap is deprecated. Use experimentRules and deliveryRules instead. return $this->experimentsMap; } diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php index ba95344b..e0bf3ebf 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php @@ -161,32 +161,37 @@ protected function getConfigEvents() */ protected function getConfigAudiences() { - $finalAudiences = []; - $uniqueIdsMap = []; - $normalAudiences = $this->projectConfig->getAudiences(); + $allAudiences = []; + $typedAudienceIds = []; + $audiences = $this->projectConfig->getAudiences(); $typedAudiences = $this->projectConfig->getTypedAudiences(); $audiencesArray = $typedAudiences; foreach ($audiencesArray as $key => $typedAudience) { - $uniqueIdsMap[$typedAudience['id']] = $typedAudience['id']; - $audiencesArray[$key]['conditions'] = json_encode($typedAudience['conditions']); - } - foreach ($normalAudiences as $naudience) { - if (!array_key_exists($naudience['id'], $uniqueIdsMap)) { - array_push($audiencesArray, $naudience); - } - } - foreach ($audiencesArray as $audience) { - $id = $audience['id']; + $typedAudienceIds[] = $typedAudience['id']; + $id = $typedAudience['id']; if ($id != '$opt_dummy_audience') { $optlyAudience = new OptimizelyAudience( $id, - $audience['name'], - $audience['conditions'] + $typedAudience['name'], + json_encode($typedAudience['conditions']) ); - array_push($finalAudiences, $optlyAudience); + array_push($allAudiences, $optlyAudience); } } - return $finalAudiences; + foreach ($audiences as $naudience) { + if (!in_array($naudience['id'], $typedAudienceIds, true)) { + $id = $naudience['id']; + if ($id != '$opt_dummy_audience') { + $optlyAudience = new OptimizelyAudience( + $id, + $naudience['name'], + $naudience['conditions'] + ); + array_push($allAudiences, $optlyAudience); + } + } + } + return $allAudiences; } /** @@ -249,7 +254,6 @@ protected function getVariablesMap(Experiment $experiment, Variation $variation) } $featureFlag = $this->experimentIdFeatureMap[$experimentId]; - $featureKey = $featureFlag->getKey(); // Set default variables for variation. @@ -331,6 +335,7 @@ protected function getVariationsMap(Experiment $experiment) */ protected function getSerializedAudiences(array $audienceConditions) { + $operators = ['and', 'or', 'not']; $finalAudiences = ''; if ($audienceConditions == null) { return $finalAudiences; @@ -340,10 +345,8 @@ protected function getSerializedAudiences(array $audienceConditions) $subAudience = ''; // Checks if item is list of conditions means if it is sub audience if (is_array($var)) { - $subAudience = $this->getSerializedAudiences($var); - - $subAudience = '(' . $subAudience . ')'; - } elseif (in_array($var, array('and', 'or', 'not'), true)) { + $subAudience = $subAudience = '(' . $this->getSerializedAudiences($var) . ')'; + } elseif (in_array($var, $operators, true)) { $cond = strtoupper(strval($var)); } else { // Checks if item is audience id @@ -353,25 +356,21 @@ protected function getSerializedAudiences(array $audienceConditions) // if audience condition is "NOT" then add "NOT" at start. Otherwise check if there is already audience id in finalAudiences then append condition between finalAudiences and item if ($finalAudiences !== '' || $cond == "NOT") { if ($finalAudiences !== '') { - $finalAudiences = $finalAudiences . ' '; - } else { - $finalAudiences = $finalAudiences; + $finalAudiences .= ' '; } if ($cond == '') { $cond = 'OR'; } - $finalAudiences = $finalAudiences . $cond . ' ' . '"' . $name . '"'; + $finalAudiences .= $cond . ' ' . '"' . $name . '"'; } else { $finalAudiences = '"' . $name . '"'; } } // Checks if sub audience is empty or not - if (strval($subAudience !== '')) { + if ($subAudience !== '') { if ($finalAudiences !== '' || $cond == "NOT") { if ($finalAudiences !== '') { - $finalAudiences = $finalAudiences . ' '; - } else { - $finalAudiences = $finalAudiences; + $finalAudiences .= ' '; } if ($cond == '') { $cond = 'OR'; @@ -426,7 +425,7 @@ protected function getExperimentsMaps() * * @param string feature rollout id. * - * @return array of optimizelyExperiments as delivery rules . + * @return array of optimizelyExperiments as delivery rules. */ protected function getDeliveryRules($rollout_id) { diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php b/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php index be8400d3..8a3a90de 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyEvent.php @@ -58,7 +58,7 @@ public function getKey() } /** - * @return string event conditions. + * @return array experimentIds representing event experiment ids. */ public function getExperimentIds() { diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php index bd674b92..69d93af6 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php @@ -29,16 +29,16 @@ class OptimizelyFeature implements \JsonSerializable private $key; /** - * Map of Experiment Keys to OptimizelyExperiments. + * Array of experiment rules. * - * @var associative array + * @var [OptimizelyExperiment] */ private $experimentRules; /** - * Map of rollout Experiments Keys to OptimizelyExperiments. + * Array of delivery rules. * - * @var associative array + * @var [OptimizelyExperiment] */ private $deliveryRules; From ccda5955cf8ddd0bfcd7607e0d06b0f45ae11db8 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Tue, 24 Aug 2021 18:19:38 +0500 Subject: [PATCH 6/9] extra spacing removed --- src/Optimizely/Config/DatafileProjectConfig.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Optimizely/Config/DatafileProjectConfig.php b/src/Optimizely/Config/DatafileProjectConfig.php index a5e47371..9663a924 100644 --- a/src/Optimizely/Config/DatafileProjectConfig.php +++ b/src/Optimizely/Config/DatafileProjectConfig.php @@ -270,8 +270,8 @@ public function __construct($datafile, $logger, $errorHandler) $this->_accountId = $config['accountId']; $this->_projectId = $config['projectId']; - $this->_anonymizeIP = isset($config['anonymizeIP']) ? $config['anonymizeIP'] : false; - $this->_botFiltering = isset($config['botFiltering']) ? $config['botFiltering'] : null; + $this->_anonymizeIP = isset($config['anonymizeIP'])? $config['anonymizeIP'] : false; + $this->_botFiltering = isset($config['botFiltering'])? $config['botFiltering'] : null; $this->_revision = $config['revision']; $this->_sendFlagDecisions = isset($config['sendFlagDecisions']) ? $config['sendFlagDecisions'] : false; From 1df4ab0b296bc91da2fcb00d7097761b0baa6bc0 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Tue, 24 Aug 2021 19:09:49 +0500 Subject: [PATCH 7/9] testcases comments addressed --- .../OptimizelyConfigServiceTest.php | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php index 667add8c..c65c2346 100644 --- a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php +++ b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php @@ -211,10 +211,17 @@ public function testGetOptimizelyConfigWithDuplicateExperimentKeys() $this->optConfigService = new OptimizelyConfigService($this->projectConfig); $optimizelyConfig = $this->optConfigService->getConfig(); $this->assertEquals(Count($optimizelyConfig->getExperimentsMap()), 1); - $experimentMapFlag1 = $optimizelyConfig->getFeaturesMap()['flag1']->getExperimentsMap(); // 9300000007569 - $experimentMapFlag2 = $optimizelyConfig->getFeaturesMap()['flag2']->getExperimentsMap(); // 9300000007573 - $this->assertEquals($experimentMapFlag1['targeted_delivery']->getId(), '9300000007569'); - $this->assertEquals($experimentMapFlag2['targeted_delivery']->getId(), '9300000007573'); + $experimentRulesFlag1 = $optimizelyConfig->getFeaturesMap()['flag1']->getExperimentRules(); // 9300000007569 + $experimentRulesFlag2 = $optimizelyConfig->getFeaturesMap()['flag2']->getExperimentRules(); // 9300000007573 + foreach ($experimentRulesFlag1 as $experimentRule) { + if ($experimentRule->getKey() == 'targeted_delivery') + $this->assertEquals($experimentRule->getId(), '9300000007569'); + } + + foreach ($experimentRulesFlag2 as $experimentRule) { + if ($experimentRule->getKey() == 'targeted_delivery') + $this->assertEquals($experimentRule->getId(), '9300000007573'); + } } public function testGetOptimizelyConfigWithDuplicateRuleKeys() @@ -301,7 +308,8 @@ public function testgetExperimentAudiences() "and", "3468206642", array('or', '3468206642', '3988293899') ) - ) + ), + array('or', '1', '100000') ]; $expectedAudienceOutputs = [ @@ -322,7 +330,8 @@ public function testgetExperimentAudiences() '(' . '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')' . ' ' . 'AND' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '(' . - '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')' + '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')', + '"'. '1'. '"' . ' '. 'OR' . ' ' . '"' . '100000' . '"' ]; for ($testNo = 0; $testNo < count($audienceConditions); $testNo++) { From 8d23eeba5b782978f8201f8f34bb80ebaeabe129 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Tue, 24 Aug 2021 23:53:23 +0500 Subject: [PATCH 8/9] lint fixed --- .../OptimizelyConfigServiceTest.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php index c65c2346..5bbaaf23 100644 --- a/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php +++ b/tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php @@ -214,13 +214,15 @@ public function testGetOptimizelyConfigWithDuplicateExperimentKeys() $experimentRulesFlag1 = $optimizelyConfig->getFeaturesMap()['flag1']->getExperimentRules(); // 9300000007569 $experimentRulesFlag2 = $optimizelyConfig->getFeaturesMap()['flag2']->getExperimentRules(); // 9300000007573 foreach ($experimentRulesFlag1 as $experimentRule) { - if ($experimentRule->getKey() == 'targeted_delivery') - $this->assertEquals($experimentRule->getId(), '9300000007569'); + if ($experimentRule->getKey() == 'targeted_delivery') { + $this->assertEquals($experimentRule->getId(), '9300000007569'); + } } foreach ($experimentRulesFlag2 as $experimentRule) { - if ($experimentRule->getKey() == 'targeted_delivery') - $this->assertEquals($experimentRule->getId(), '9300000007573'); + if ($experimentRule->getKey() == 'targeted_delivery') { + $this->assertEquals($experimentRule->getId(), '9300000007573'); + } } } @@ -331,7 +333,7 @@ public function testgetExperimentAudiences() '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')' . ' ' . 'AND' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'AND' . ' ' . '(' . '"' . "exactString" . '"' . ' ' . 'OR' . ' ' . '"' . '$$dummyExists' . '"' . ')' . ')', - '"'. '1'. '"' . ' '. 'OR' . ' ' . '"' . '100000' . '"' + '"'. '1'. '"' . ' '. 'OR' . ' ' . '"' . '100000' . '"' ]; for ($testNo = 0; $testNo < count($audienceConditions); $testNo++) { From 530477838f803ea508506e351abd9f834e5c163f Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Wed, 25 Aug 2021 02:20:36 +0500 Subject: [PATCH 9/9] comments addressed --- src/Optimizely/OptimizelyConfig/OptimizelyConfig.php | 1 - src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php | 2 +- src/Optimizely/OptimizelyConfig/OptimizelyFeature.php | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php index 1ac14244..26093779 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfig.php @@ -128,7 +128,6 @@ public function getDatafile() */ public function getExperimentsMap() { - # This experimentsMap is deprecated. Use experimentRules and deliveryRules instead. return $this->experimentsMap; } diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php index e0bf3ebf..d2dadb7c 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php @@ -345,7 +345,7 @@ protected function getSerializedAudiences(array $audienceConditions) $subAudience = ''; // Checks if item is list of conditions means if it is sub audience if (is_array($var)) { - $subAudience = $subAudience = '(' . $this->getSerializedAudiences($var) . ')'; + $subAudience = '(' . $this->getSerializedAudiences($var) . ')'; } elseif (in_array($var, $operators, true)) { $cond = strtoupper(strval($var)); } else { diff --git a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php index 69d93af6..7b605064 100644 --- a/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php +++ b/src/Optimizely/OptimizelyConfig/OptimizelyFeature.php @@ -103,6 +103,7 @@ public function getDeliveryRules() */ public function getExperimentsMap() { + # This experimentsMap is deprecated. Use experimentRules and deliveryRules instead. return $this->experimentsMap; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy