|  | 
| 16 | 16 | use LaunchDarkly\Subsystems\FeatureRequester; | 
| 17 | 17 | use Psr\Log\LoggerInterface; | 
| 18 | 18 | 
 | 
| 19 |  | -/** | 
| 20 |  | - * @ignore | 
| 21 |  | - * @internal | 
| 22 |  | - */ | 
| 23 |  | -class EvaluatorState | 
| 24 |  | -{ | 
| 25 |  | - public ?array $prerequisiteStack = null; | 
| 26 |  | - public ?array $segmentStack = null; | 
| 27 |  | -  | 
| 28 |  | - public function __construct(public FeatureFlag $originalFlag) | 
| 29 |  | - { | 
| 30 |  | - } | 
| 31 |  | -} | 
| 32 |  | - | 
| 33 | 19 | /** | 
| 34 | 20 |  * Encapsulates the feature flag evaluation logic. The Evaluator has no direct access to the | 
| 35 | 21 |  * rest of the SDK environment; if it needs to retrieve flags or segments that are referenced | 
| @@ -62,15 +48,15 @@ public function __construct(FeatureRequester $featureRequester, ?LoggerInterface | 
| 62 | 48 |  */ | 
| 63 | 49 |  public function evaluate(FeatureFlag $flag, LDContext $context, ?callable $prereqEvalSink): EvalResult | 
| 64 | 50 |  { | 
| 65 |  | - $stateStack = null; | 
| 66 | 51 |  $state = new EvaluatorState($flag); | 
| 67 | 52 |  try { | 
| 68 |  | - return $this->evaluateInternal($flag, $context, $prereqEvalSink, $state); | 
|  | 53 | + return $this->evaluateInternal($flag, $context, $prereqEvalSink, $state) | 
|  | 54 | + ->withState($state); | 
| 69 | 55 |  } catch (EvaluationException $e) { | 
| 70 |  | - return new EvalResult(new EvaluationDetail(null, null, EvaluationReason::error($e->getErrorKind()))); | 
|  | 56 | + return new EvalResult(new EvaluationDetail(null, null, EvaluationReason::error($e->getErrorKind())), false, $state); | 
| 71 | 57 |  } catch (\Throwable $e) { | 
| 72 | 58 |  Util::logExceptionAtErrorLevel($this->_logger, $e, 'Unexpected error when evaluating flag ' . $flag->getKey()); | 
| 73 |  | - return new EvalResult(new EvaluationDetail(null, null, EvaluationReason::error(EvaluationReason::EXCEPTION_ERROR))); | 
|  | 59 | + return new EvalResult(new EvaluationDetail(null, null, EvaluationReason::error(EvaluationReason::EXCEPTION_ERROR)), false, $state); | 
| 74 | 60 |  } | 
| 75 | 61 |  } | 
| 76 | 62 | 
 | 
| @@ -144,14 +130,25 @@ private function checkPrerequisites( | 
| 144 | 130 |  EvaluationReason::MALFORMED_FLAG_ERROR | 
| 145 | 131 |  ); | 
| 146 | 132 |  } | 
|  | 133 | + | 
|  | 134 | + if ($state->depth == 0) { | 
|  | 135 | + if ($state->prerequisites === null) { | 
|  | 136 | + $state->prerequisites = []; | 
|  | 137 | + } | 
|  | 138 | + $state->prerequisites[] = $prereqKey; | 
|  | 139 | + } | 
|  | 140 | + | 
|  | 141 | + | 
| 147 | 142 |  $prereqOk = true; | 
| 148 | 143 |  $prereqFeatureFlag = $this->_featureRequester->getFeature($prereqKey); | 
| 149 | 144 |  if ($prereqFeatureFlag === null) { | 
| 150 | 145 |  $prereqOk = false; | 
| 151 | 146 |  } else { | 
| 152 | 147 |  // Note that if the prerequisite flag is off, we don't consider it a match no matter what its | 
| 153 | 148 |  // off variation was. But we still need to evaluate it in order to generate an event. | 
|  | 149 | + $state->depth++; | 
| 154 | 150 |  $prereqEvalResult = $this->evaluateInternal($prereqFeatureFlag, $context, $prereqEvalSink, $state); | 
|  | 151 | + $state->depth--; | 
| 155 | 152 |  $variation = $prereq->getVariation(); | 
| 156 | 153 |  if (!$prereqFeatureFlag->isOn() || $prereqEvalResult->getDetail()->getVariationIndex() !== $variation) { | 
| 157 | 154 |  $prereqOk = false; | 
|  | 
0 commit comments