Skip to content

Commit 516f36f

Browse files
authored
fix(ap-2131): incorrect payload format of data requests (#17)
* fix(ap-2131): incorrect payload format of data requests * chore: bump version * chore: correct code comments * refactor: include polyfill into flatten util
1 parent f6ac282 commit 516f36f

File tree

8 files changed

+136
-62
lines changed

8 files changed

+136
-62
lines changed

App/Beacon.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Evolv\EvolvContext;
88

99

10+
const CLIENT_NAME = 'php-sdk';
1011
const ENDPOINT_PATTERN = "/\/(v\d+)\/\w+\/([a-z]+)$/i";
1112

1213
class Beacon {
@@ -53,7 +54,7 @@ private function wrapMessages()
5354
{
5455
return [
5556
'uid' => $this->context->uid,
56-
'client' => 'php-sdk',
57+
'client' => CLIENT_NAME,
5758
'messages' => $this->messages
5859
];
5960
}
@@ -67,7 +68,7 @@ private function transmit()
6768
if ($this->v1Events) {
6869
foreach($this->messages as $message) {
6970
$editedMessage = $message;
70-
$editedMessage = $message['payload'] ?? [];
71+
$editedMessage = $message['payload'];
7172
$editedMessage['type'] = $message['type'];
7273

7374
$this->send($editedMessage);
@@ -81,7 +82,7 @@ public function emit(string $type, $payload, bool $flush = false)
8182
{
8283
$this->messages[] = [
8384
'type' => $type,
84-
'payload' => $payload,
85+
'payload' => empty($payload) ? new \stdClass() : $payload,
8586
'timestamp' => time()
8687
];
8788

@@ -91,4 +92,4 @@ public function emit(string $type, $payload, bool $flush = false)
9192
public function flush() {
9293
$this->transmit();
9394
}
94-
}
95+
}

App/EvolvClient.php

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
use function Evolv\Utils\waitFor;
88
use function Evolv\Utils\emit;
99

10-
10+
/**
11+
* The EvolvClient provides a low level integration with the Evolv participant APIs.
12+
*
13+
* The client provides asynchronous access to key states, values, contexts, and configurations.
14+
*
15+
*/
1116
class EvolvClient
1217
{
1318
const INITIALIZED = 'initialized';
@@ -16,16 +21,19 @@ class EvolvClient
1621
const EVENT_EMITTED = 'event.emitted';
1722

1823
public bool $initialized = false;
24+
/**
25+
* The context against which the key predicates will be evaluated.
26+
*/
1927
public EvolvContext $context;
2028
private $store;
2129
private bool $autoconfirm;
2230
private Beacon $contextBeacon;
2331
private Beacon $eventBeacon;
2432

2533
/**
26-
* @param string $environment
27-
* @param string $endpoint
28-
* @param bool $autoconfirm
34+
* @param string $environment The current environment id.
35+
* @param string $endpoint The participants API endpoint.
36+
* @param bool $autoconfirm Optional. True by default. The autoconfirm flag.
2937
* @return object
3038
*/
3139
public function __construct(string $environment, string $endpoint = 'https://participants.evolv.ai/', bool $autoconfirm = true)
@@ -44,14 +52,11 @@ public function __construct(string $environment, string $endpoint = 'https://par
4452
* Initializes the client with required context information.
4553
*
4654
* @param string $uid A globally unique identifier for the current participant.
47-
* @param object $remoteContext A map of data used for evaluating context predicates and analytics.
48-
* @param object $localContext A map of data used only for evaluating context predicates.
49-
* @return array
55+
* @param array $remoteContext A map of data used for evaluating context predicates and analytics.
56+
* @param array $localContext A map of data used only for evaluating context predicates.
5057
*/
51-
5258
public function initialize(string $uid, array $remoteContext = [], array $localContext = [], $httpClient = null)
5359
{
54-
5560
if ($this->initialized) {
5661
throw new \Exception('Evolv: Client is already initialized');
5762
exit('Evolv: Client is already initialized');
@@ -62,15 +67,6 @@ public function initialize(string $uid, array $remoteContext = [], array $localC
6267
exit('Evolv: "uid" must be specified');
6368
}
6469

65-
$this->context->initialize($uid, $remoteContext, $localContext);
66-
$this->store->initialize($this->context, $httpClient);
67-
68-
if ($this->autoconfirm) {
69-
$this->confirm();
70-
}
71-
72-
$this->initialized = true;
73-
7470
waitFor(CONTEXT_INITIALIZED, function($type, $ctx) {
7571
$this->contextBeacon->emit($type, $this->context->remoteContext);
7672
});
@@ -81,12 +77,27 @@ public function initialize(string $uid, array $remoteContext = [], array $localC
8177
}
8278
$this->contextBeacon->emit($type, ['key' => $key, 'value' => $value]);
8379
});
84-
waitFor(CONTEXT_VALUE_CHANGED, function($type, $key, $value, $local) {
80+
waitFor(CONTEXT_VALUE_CHANGED, function($type, $key, $value, $before, $local) {
8581
if ($local) {
8682
return;
8783
}
8884
$this->contextBeacon->emit($type, ['key' => $key, 'value' => $value]);
8985
});
86+
waitFor(CONTEXT_VALUE_REMOVED, function ($type, $key, $local) {
87+
if ($local) {
88+
return;
89+
}
90+
$this->contextBeacon->emit($type, ['key' => $key]);
91+
});
92+
93+
$this->context->initialize($uid, $remoteContext, $localContext);
94+
$this->store->initialize($this->context, $httpClient);
95+
96+
if ($this->autoconfirm) {
97+
$this->confirm();
98+
}
99+
100+
$this->initialized = true;
90101

91102
emit(EvolvClient::INITIALIZED);
92103
}
@@ -117,8 +128,7 @@ public function initialize(string $uid, array $remoteContext = [], array $localC
117128
*
118129
* @param string $topic The event topic on which the listener should be invoked.
119130
* @param callable $listener The listener to be invoked for the specified topic.
120-
* @function
121-
* @see EvolvClient for listeners that should only be invoked once.
131+
* @see EvolvClient for listeners that should only be invoked once.
122132
*/
123133
public function on(string $topic, callable $listener)
124134
{
@@ -129,8 +139,8 @@ public function on(string $topic, callable $listener)
129139
* Send an event to the events endpoint.
130140
*
131141
* @param string $type The type associated with the event.
132-
* @param object $metadata Any metadata to attach to the event.
133-
* @param boolean $flush If true, the event will be sent immediately.
142+
* @param mixed $metadata Any metadata to attach to the event.
143+
* @param bool $flush If true, the event will be sent immediately.
134144
*/
135145
public function emit(string $type, $metadata, bool $flush = false)
136146
{
@@ -146,9 +156,8 @@ public function emit(string $type, $metadata, bool $flush = false)
146156
* Check all active keys that start with the specified prefix.
147157
*
148158
* @param string $prefix The prefix of the keys to check.
149-
* @returns {SubscribablePromise.<Object|Error>} A SubscribablePromise that resolves to object
150-
* describing the state of active keys.
151-
* @function
159+
* @param callable $listener Optional. The callback function to listen to active keys changes.
160+
* @return array An array describing the state of active keys.
152161
*/
153162
public function getActiveKeys(string $prefix = '', callable $listener = null)
154163
{
@@ -159,8 +168,8 @@ public function getActiveKeys(string $prefix = '', callable $listener = null)
159168
* Get the value of a specified key.
160169
*
161170
* @param string $key The key of the value to retrieve.
162-
* @returns @mixed A value of the specified key.
163-
* @function
171+
* @param callable $listener Optional. The callback function to listen to the specified key changes.
172+
* @return mixed A value of the specified key.
164173
*/
165174
public function get(string $key = '', callable $listener = null)
166175
{
@@ -229,12 +238,12 @@ public function confirm()
229238
* Marks a consumer as unsuccessfully retrieving and / or applying requested values, making them ineligible for
230239
* inclusion in optimization statistics.
231240
*
232-
* @param object $details Optional. Information on the reason for contamination. If provided, the object should
241+
* @param array $details Optional. Information on the reason for contamination. If provided, the object should
233242
* contain a reason. Optionally, a 'details' value should be included for extra debugging info
234-
* @param boolean $allExperiments If true, the user will be excluded from all optimizations, including optimization
243+
* @param bool $allExperiments If true, the user will be excluded from all optimizations, including optimization
235244
* not applicable to this page
236245
*/
237-
public function contaminate($details, bool $allExperiments = false)
246+
public function contaminate(array $details, bool $allExperiments = false)
238247
{
239248
$allocations = $this->context->get('experiments.allocations');
240249
if (!isset($allocations) || !count($allocations)) {
@@ -281,10 +290,3 @@ public function contaminate($details, bool $allExperiments = false)
281290
emit(EvolvClient::CONTAMINATED);
282291
}
283292
}
284-
285-
286-
287-
288-
289-
290-

App/EvolvContext.php

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,28 @@
2525

2626
const DEFAULT_QUEUE_LIMIT = 50;
2727

28+
/**
29+
* The EvolvContext provides functionality to manage data relating to the client state, or context in which the
30+
* variants will be applied.
31+
*
32+
* This data is used for determining which variables are active, and for general analytics.
33+
*
34+
*/
2835
class EvolvContext
2936
{
37+
/**
38+
* A unique identifier for the participant.
39+
*/
3040
public string $uid;
41+
42+
/**
43+
* The context information for evaluation of predicates and analytics.
44+
*/
3145
public array $remoteContext = [];
46+
47+
/**
48+
* The context information for evaluation of predicates only, and not used for analytics.
49+
*/
3250
public array $localContext = [];
3351
private bool $initialized = false;
3452

@@ -39,6 +57,11 @@ private function ensureInitialized(): void
3957
}
4058
}
4159

60+
/**
61+
* Computes the effective context from the local and remote contexts.
62+
*
63+
* @return array The effective context from the local and remote contexts.
64+
*/
4265
public function resolve()
4366
{
4467
$this->ensureInitialized();
@@ -109,7 +132,7 @@ public function set(string $key, $value, bool $local = false): void
109132
* Retrieve a value from the context.
110133
*
111134
* @param string $key The kay associated with the value to retrieve.
112-
* @returns {*} The value associated with the specified key.
135+
* @return mixed The value associated with the specified key.
113136
*/
114137
public function get(string $key)
115138
{
@@ -128,7 +151,7 @@ public function get(string $key)
128151
*
129152
* Note: This will cause the effective genome to be recomputed.
130153
*
131-
* @param string $key The key to remove from the context.
154+
* @param string $key The key to remove from the context.
132155
*/
133156
public function remove(string $key)
134157
{
@@ -152,7 +175,7 @@ public function remove(string $key)
152175
* Note: This will cause the effective genome to be recomputed.
153176
*
154177
* @param array $update The values to update the context with.
155-
* @param boolean $local If true, the values will only be added to the localContext.
178+
* @param bool $local If true, the values will only be added to the localContext.
156179
*/
157180
public function update(array $update, $local = false) {
158181
$this->ensureInitialized();
@@ -187,8 +210,8 @@ public function update(array $update, $local = false) {
187210
/**
188211
* Checks if the specified key is currently defined in the context.
189212
*
190-
* @param $key The key to check.
191-
* @returns bool True if the key has an associated value in the context.
213+
* @param string $key The key to check.
214+
* @return bool True if the key has an associated value in the context.
192215
*/
193216
public function contains(string $key)
194217
{
@@ -202,9 +225,9 @@ public function contains(string $key)
202225
*
203226
* @param string $key The array to add to.
204227
* @param array $value Value to add to the array.
205-
* @param bool $local {Boolean} If true, the value will only be added to the localContext.
206-
* @param int $limit {Number} Max length of array to maintain.
207-
* @returns boolean True if value was successfully added.
228+
* @param bool $local If true, the value will only be added to the localContext.
229+
* @param int $limit Max length of array to maintain.
230+
* @return bool True if value was successfully added.
208231
*/
209232
public function pushToArray(string $key, $value, $local = false, $limit = null)
210233
{

App/Predicate.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,19 @@ private function evaluatePredicate($context, $predicate, array &$passedRules, ar
124124

125125
/**
126126
* @typedef EvaluationResult
127-
* @property {Set<object>} passed
128-
* @property {Set<object>} failed
129-
* @property {boolean} rejected
130-
* @property {Set<string>} touched
127+
* @property array passed
128+
* @property array failed
129+
* @property array rejected
130+
* @property array touched
131131
*/
132132

133133
/**
134134
* Evaluates a query against a user object and saves passing/failing rule ids to provided sets.
135-
* @param context A context object containing describing the context the predicate should be evaluated against.
136-
* @param predicate Nested predicate object that rules structured into groups as a deeply nested tree.
135+
* @param array context A context object containing describing the context the predicate should be evaluated against.
136+
* @param array predicate Nested predicate object that rules structured into groups as a deeply nested tree.
137137
* note: There is no set limit to the depth of this tree, hence we must work with it
138138
* using recursion.
139-
* @returns {EvaluationResult}
139+
* @return {EvaluationResult}
140140
*/
141141
public function evaluate($context, $predicate)
142142
{

App/Utils/flatten.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,25 @@
22

33
namespace Evolv\Utils;
44

5+
if (!function_exists('array_is_list')) {
6+
function array_is_list(array $array): bool
7+
{
8+
$i = 0;
9+
foreach ($array as $k => $v) {
10+
if ($k !== $i++) {
11+
return false;
12+
}
13+
}
14+
return true;
15+
}
16+
}
17+
518
function flatten_recursive(array $current, string $parentKey) {
619
$items = [];
720

821
foreach($current as $key => $value) {
922
$newKey = $parentKey ? ($parentKey . '.' . $key) : $key;
10-
if (is_array($value)) {
23+
if (is_array($value) && !array_is_list($value)) {
1124
$items = array_merge($items, flatten_recursive($current[$key], $newKey));
1225
} else {
1326
$items[$newKey] = $value;
@@ -19,4 +32,4 @@ function flatten_recursive(array $current, string $parentKey) {
1932

2033
function flatten(array $array) {
2134
return flatten_recursive($array, '');
22-
}
35+
}

Example/index.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
declare (strict_types=1);
44

5-
use Evolv\EvolvClient;
5+
ini_set('display_errors', '1');
6+
ini_set('display_startup_errors', '1');
7+
error_reporting(E_ALL);
8+
9+
use Evolv\EvolvClient;
610

711
require_once __DIR__ . '/../vendor/autoload.php';
812

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "evolv/php-sdk",
33
"description": "This is the official PHP client for the Evolv AI optimization platform.",
44
"type": "library",
5-
"version": "1.0.2",
5+
"version": "1.0.3",
66
"require-dev": {
77
"phpunit/phpunit": "^9.5",
88
"phpdocumentor/phpdocumentor": "v3.1.2",

0 commit comments

Comments
 (0)