19
19
20
20
use Monolog \Logger ;
21
21
use Optimizely \Enums \CommonAudienceEvaluationLogs as logs ;
22
+ use Optimizely \Utils \SemVersionConditionEvaluator ;
22
23
use Optimizely \Utils \Validator ;
23
24
24
25
class CustomAttributeConditionEvaluator
@@ -27,13 +28,20 @@ class CustomAttributeConditionEvaluator
27
28
28
29
const EXACT_MATCH_TYPE = 'exact ' ;
29
30
const EXISTS_MATCH_TYPE = 'exists ' ;
31
+ const GREATER_THAN_EQUAL_TO_MATCH_TYPE = 'ge ' ;
30
32
const GREATER_THAN_MATCH_TYPE = 'gt ' ;
33
+ const LESS_THAN_EQUAL_TO_MATCH_TYPE = 'le ' ;
31
34
const LESS_THAN_MATCH_TYPE = 'lt ' ;
35
+ const SEMVER_EQ = 'semver_eq ' ;
36
+ const SEMVER_GE = 'semver_ge ' ;
37
+ const SEMVER_GT = 'semver_gt ' ;
38
+ const SEMVER_LE = 'semver_le ' ;
39
+ const SEMVER_LT = 'semver_lt ' ;
32
40
const SUBSTRING_MATCH_TYPE = 'substring ' ;
33
41
34
42
/**
35
43
* @var UserAttributes
36
- */
44
+ */
37
45
protected $ userAttributes ;
38
46
39
47
/**
@@ -57,7 +65,7 @@ protected function setNullForMissingKeys(array $leafCondition)
57
65
{
58
66
$ keys = ['type ' , 'match ' , 'value ' ];
59
67
foreach ($ keys as $ key ) {
60
- $ leafCondition [$ key ] = isset ($ leafCondition [$ key ]) ? $ leafCondition [$ key ]: null ;
68
+ $ leafCondition [$ key ] = isset ($ leafCondition [$ key ]) ? $ leafCondition [$ key ] : null ;
61
69
}
62
70
63
71
return $ leafCondition ;
@@ -70,8 +78,20 @@ protected function setNullForMissingKeys(array $leafCondition)
70
78
*/
71
79
protected function getMatchTypes ()
72
80
{
73
- return array (self ::EXACT_MATCH_TYPE , self ::EXISTS_MATCH_TYPE , self ::GREATER_THAN_MATCH_TYPE ,
74
- self ::LESS_THAN_MATCH_TYPE , self ::SUBSTRING_MATCH_TYPE );
81
+ return array (
82
+ self ::EXACT_MATCH_TYPE ,
83
+ self ::EXISTS_MATCH_TYPE ,
84
+ self ::GREATER_THAN_EQUAL_TO_MATCH_TYPE ,
85
+ self ::GREATER_THAN_MATCH_TYPE ,
86
+ self ::LESS_THAN_EQUAL_TO_MATCH_TYPE ,
87
+ self ::LESS_THAN_MATCH_TYPE ,
88
+ self ::SEMVER_EQ ,
89
+ self ::SEMVER_GE ,
90
+ self ::SEMVER_GT ,
91
+ self ::SEMVER_LE ,
92
+ self ::SEMVER_LT ,
93
+ self ::SUBSTRING_MATCH_TYPE ,
94
+ );
75
95
}
76
96
77
97
/**
@@ -86,8 +106,15 @@ protected function getEvaluatorByMatchType($matchType)
86
106
$ evaluatorsByMatchType = array ();
87
107
$ evaluatorsByMatchType [self ::EXACT_MATCH_TYPE ] = 'exactEvaluator ' ;
88
108
$ evaluatorsByMatchType [self ::EXISTS_MATCH_TYPE ] = 'existsEvaluator ' ;
109
+ $ evaluatorsByMatchType [self ::GREATER_THAN_EQUAL_TO_MATCH_TYPE ] = 'greaterThanEqualToEvaluator ' ;
89
110
$ evaluatorsByMatchType [self ::GREATER_THAN_MATCH_TYPE ] = 'greaterThanEvaluator ' ;
111
+ $ evaluatorsByMatchType [self ::LESS_THAN_EQUAL_TO_MATCH_TYPE ] = 'lessThanEqualToEvaluator ' ;
90
112
$ evaluatorsByMatchType [self ::LESS_THAN_MATCH_TYPE ] = 'lessThanEvaluator ' ;
113
+ $ evaluatorsByMatchType [self ::SEMVER_EQ ] = 'semverEqualEvaluator ' ;
114
+ $ evaluatorsByMatchType [self ::SEMVER_GE ] = 'semverGreaterThanEqualToEvaluator ' ;
115
+ $ evaluatorsByMatchType [self ::SEMVER_GT ] = 'semverGreaterThanEvaluator ' ;
116
+ $ evaluatorsByMatchType [self ::SEMVER_LE ] = 'semverLessThanEqualToEvaluator ' ;
117
+ $ evaluatorsByMatchType [self ::SEMVER_LT ] = 'semverLessThanEvaluator ' ;
91
118
$ evaluatorsByMatchType [self ::SUBSTRING_MATCH_TYPE ] = 'substringEvaluator ' ;
92
119
93
120
return $ evaluatorsByMatchType [$ matchType ];
@@ -109,6 +136,33 @@ protected function isValueTypeValidForExactConditions($value)
109
136
return false ;
110
137
}
111
138
139
+ /**
140
+ * Returns result of SemVersionConditionEvaluator::compareVersion for given target and user versions.
141
+ *
142
+ * @param object $condition
143
+ *
144
+ * @return null|int 0 if user's version attribute is equal to the semver condition value,
145
+ * 1 if user's version attribute is greater than the semver condition value,
146
+ * -1 if user's version attribute is less than the semver condition value,
147
+ * null if the condition value or user attribute value has an invalid type, or
148
+ * if there is a mismatch between the user attribute type and the condition
149
+ * value type.
150
+ */
151
+ protected function semverEvaluator ($ condition )
152
+ {
153
+ $ conditionName = $ condition ['name ' ];
154
+ $ conditionValue = $ condition ['value ' ];
155
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
156
+
157
+ if (!Validator::validateNonEmptyString ($ conditionValue ) || !Validator::validateNonEmptyString ($ userValue )) {
158
+ $ this ->logger ->log (Logger::WARNING , sprintf (
159
+ logs::ATTRIBUTE_FORMAT_INVALID
160
+ ));
161
+ return null ;
162
+ }
163
+ return SemVersionConditionEvaluator::compareVersion ($ conditionValue , $ userValue , $ this ->logger );
164
+ }
165
+
112
166
/**
113
167
* Evaluate the given exact match condition for the given user attributes.
114
168
*
@@ -124,7 +178,7 @@ protected function exactEvaluator($condition)
124
178
{
125
179
$ conditionName = $ condition ['name ' ];
126
180
$ conditionValue = $ condition ['value ' ];
127
- $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ]: null ;
181
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
128
182
129
183
if (!$ this ->isValueTypeValidForExactConditions ($ conditionValue ) ||
130
184
((is_int ($ conditionValue ) || is_float ($ conditionValue )) && !Validator::isFiniteNumber ($ conditionValue ))) {
@@ -189,7 +243,7 @@ protected function greaterThanEvaluator($condition)
189
243
{
190
244
$ conditionName = $ condition ['name ' ];
191
245
$ conditionValue = $ condition ['value ' ];
192
- $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ]: null ;
246
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
193
247
194
248
if (!Validator::isFiniteNumber ($ conditionValue )) {
195
249
$ this ->logger ->log (Logger::WARNING , sprintf (
@@ -221,6 +275,52 @@ protected function greaterThanEvaluator($condition)
221
275
return $ userValue > $ conditionValue ;
222
276
}
223
277
278
+ /**
279
+ * Evaluate the given greater than equal to match condition for the given user attributes.
280
+ *
281
+ * @param object $condition
282
+ *
283
+ * @return boolean true if the user attribute value is greater than or equal to the condition value,
284
+ * false if the user attribute value is less than the condition value,
285
+ * null if the condition value isn't a number or the user attribute value
286
+ * isn't a number.
287
+ */
288
+ protected function greaterThanEqualToEvaluator ($ condition )
289
+ {
290
+ $ conditionName = $ condition ['name ' ];
291
+ $ conditionValue = $ condition ['value ' ];
292
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
293
+
294
+ if (!Validator::isFiniteNumber ($ conditionValue )) {
295
+ $ this ->logger ->log (Logger::WARNING , sprintf (
296
+ logs::UNKNOWN_CONDITION_VALUE ,
297
+ json_encode ($ condition )
298
+ ));
299
+ return null ;
300
+ }
301
+
302
+ if (!(is_int ($ userValue ) || is_float ($ userValue ))) {
303
+ $ this ->logger ->log (Logger::WARNING , sprintf (
304
+ logs::UNEXPECTED_TYPE ,
305
+ json_encode ($ condition ),
306
+ gettype ($ userValue ),
307
+ $ conditionName
308
+ ));
309
+ return null ;
310
+ }
311
+
312
+ if (!Validator::isFiniteNumber ($ userValue )) {
313
+ $ this ->logger ->log (Logger::WARNING , sprintf (
314
+ logs::INFINITE_ATTRIBUTE_VALUE ,
315
+ json_encode ($ condition ),
316
+ $ conditionName
317
+ ));
318
+ return null ;
319
+ }
320
+
321
+ return $ userValue >= $ conditionValue ;
322
+ }
323
+
224
324
/**
225
325
* Evaluate the given less than match condition for the given user attributes.
226
326
*
@@ -235,7 +335,7 @@ protected function lessThanEvaluator($condition)
235
335
{
236
336
$ conditionName = $ condition ['name ' ];
237
337
$ conditionValue = $ condition ['value ' ];
238
- $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ]: null ;
338
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
239
339
240
340
if (!Validator::isFiniteNumber ($ conditionValue )) {
241
341
$ this ->logger ->log (Logger::WARNING , sprintf (
@@ -267,7 +367,53 @@ protected function lessThanEvaluator($condition)
267
367
return $ userValue < $ conditionValue ;
268
368
}
269
369
270
- /**
370
+ /**
371
+ * Evaluate the given less than equal to match condition for the given user attributes.
372
+ *
373
+ * @param object $condition
374
+ *
375
+ * @return boolean true if the user attribute value is less than or equal to the condition value,
376
+ * false if the user attribute value is greater than the condition value,
377
+ * null if the condition value isn't a number or the user attribute value
378
+ * isn't a number.
379
+ */
380
+ protected function lessThanEqualToEvaluator ($ condition )
381
+ {
382
+ $ conditionName = $ condition ['name ' ];
383
+ $ conditionValue = $ condition ['value ' ];
384
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
385
+
386
+ if (!Validator::isFiniteNumber ($ conditionValue )) {
387
+ $ this ->logger ->log (Logger::WARNING , sprintf (
388
+ logs::UNKNOWN_CONDITION_VALUE ,
389
+ json_encode ($ condition )
390
+ ));
391
+ return null ;
392
+ }
393
+
394
+ if (!(is_int ($ userValue ) || is_float ($ userValue ))) {
395
+ $ this ->logger ->log (Logger::WARNING , sprintf (
396
+ logs::UNEXPECTED_TYPE ,
397
+ json_encode ($ condition ),
398
+ gettype ($ userValue ),
399
+ $ conditionName
400
+ ));
401
+ return null ;
402
+ }
403
+
404
+ if (!Validator::isFiniteNumber ($ userValue )) {
405
+ $ this ->logger ->log (Logger::WARNING , sprintf (
406
+ logs::INFINITE_ATTRIBUTE_VALUE ,
407
+ json_encode ($ condition ),
408
+ $ conditionName
409
+ ));
410
+ return null ;
411
+ }
412
+
413
+ return $ userValue <= $ conditionValue ;
414
+ }
415
+
416
+ /**
271
417
* Evaluate the given substring than match condition for the given user attributes.
272
418
*
273
419
* @param object $condition
@@ -281,7 +427,7 @@ protected function substringEvaluator($condition)
281
427
{
282
428
$ conditionName = $ condition ['name ' ];
283
429
$ conditionValue = $ condition ['value ' ];
284
- $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ]: null ;
430
+ $ userValue = isset ($ this ->userAttributes [$ conditionName ]) ? $ this ->userAttributes [$ conditionName ] : null ;
285
431
286
432
if (!is_string ($ conditionValue )) {
287
433
$ this ->logger ->log (Logger::WARNING , sprintf (
@@ -304,6 +450,96 @@ protected function substringEvaluator($condition)
304
450
return strpos ($ userValue , $ conditionValue ) !== false ;
305
451
}
306
452
453
+ /**
454
+ * Evaluate the given semantic version equal match condition for the given user attributes.
455
+ *
456
+ * @param object $condition
457
+ *
458
+ * @return boolean true if user's version attribute is equal to the semver condition value,
459
+ * false if the user's version attribute is greater or less than the semver condition value,
460
+ * null if the semver condition value or user's version attribute is invalid.
461
+ */
462
+ protected function semverEqualEvaluator ($ condition )
463
+ {
464
+ $ comparison = $ this ->semverEvaluator ($ condition );
465
+ if ($ comparison === null ) {
466
+ return null ;
467
+ }
468
+ return $ comparison === 0 ;
469
+ }
470
+
471
+ /**
472
+ * Evaluate the given semantic version greater than match condition for the given user attributes.
473
+ *
474
+ * @param object $condition
475
+ *
476
+ * @return boolean true if user's version attribute is greater than the semver condition value,
477
+ * false if the user's version attribute is less than or equal to the semver condition value,
478
+ * null if the semver condition value or user's version attribute is invalid.
479
+ */
480
+ protected function semverGreaterThanEvaluator ($ condition )
481
+ {
482
+ $ comparison = $ this ->semverEvaluator ($ condition );
483
+ if ($ comparison === null ) {
484
+ return null ;
485
+ }
486
+ return $ comparison > 0 ;
487
+ }
488
+
489
+ /**
490
+ * Evaluate the given semantic version greater than equal to match condition for the given user attributes.
491
+ *
492
+ * @param object $condition
493
+ *
494
+ * @return boolean true if user's version attribute is greater than or equal to the semver condition value,
495
+ * false if the user's version attribute is less than the semver condition value,
496
+ * null if the semver condition value or user's version attribute is invalid.
497
+ */
498
+ protected function semverGreaterThanEqualToEvaluator ($ condition )
499
+ {
500
+ $ comparison = $ this ->semverEvaluator ($ condition );
501
+ if ($ comparison === null ) {
502
+ return null ;
503
+ }
504
+ return $ comparison >= 0 ;
505
+ }
506
+
507
+ /**
508
+ * Evaluate the given semantic version less than match condition for the given user attributes.
509
+ *
510
+ * @param object $condition
511
+ *
512
+ * @return boolean true if user's version attribute is less than the semver condition value,
513
+ * false if the user's version attribute is greater than or equal to the semver condition value,
514
+ * null if the semver condition value or user's version attribute is invalid.
515
+ */
516
+ protected function semverLessThanEvaluator ($ condition )
517
+ {
518
+ $ comparison = $ this ->semverEvaluator ($ condition );
519
+ if ($ comparison === null ) {
520
+ return null ;
521
+ }
522
+ return $ comparison < 0 ;
523
+ }
524
+
525
+ /**
526
+ * Evaluate the given semantic version less than equal to match condition for the given user attributes.
527
+ *
528
+ * @param object $condition
529
+ *
530
+ * @return boolean true if user's version attribute is less than or equal to the semver condition value,
531
+ * false if the user's version attribute is greater than the semver condition value,
532
+ * null if the semver condition value or user's version attribute is invalid.
533
+ */
534
+ protected function semverLessThanEqualToEvaluator ($ condition )
535
+ {
536
+ $ comparison = $ this ->semverEvaluator ($ condition );
537
+ if ($ comparison === null ) {
538
+ return null ;
539
+ }
540
+ return $ comparison <= 0 ;
541
+ }
542
+
307
543
/**
308
544
* Function to evaluate audience conditions against user's attributes.
309
545
*
0 commit comments