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; }