original = $original; $this->expectation = $this->expect($value); } /** * Creates the opposite expectation for the value. */ public function not(): HigherOrderExpectation { $this->opposite = !$this->opposite; return $this; } /** * Dynamically calls methods on the class with the given arguments. * * @param array $arguments */ public function __call(string $name, array $arguments): self { if (!$this->expectationHasMethod($name)) { /* @phpstan-ignore-next-line */ return new self($this->original, $this->getValue()->$name(...$arguments)); } return $this->performAssertion($name, $arguments); } /** * Accesses properties in the value or in the expectation. */ public function __get(string $name): self { if ($name === 'not') { return $this->not(); } if (!$this->expectationHasMethod($name)) { return new self($this->original, $this->retrieve($name, $this->getValue())); } return $this->performAssertion($name, []); } /** * Determines if the original expectation has the given method name. */ private function expectationHasMethod(string $name): bool { return method_exists($this->original, $name) || $this->original::hasExtend($name); } /** * Retrieve the applicable value based on the current reset condition. * * @return mixed */ private function getValue() { return $this->shouldReset ? $this->original->value : $this->expectation->value; } /** * Performs the given assertion with the current expectation. * * @param array $arguments */ private function performAssertion(string $name, array $arguments): self { /* @phpstan-ignore-next-line */ $this->expectation = ($this->opposite ? $this->expectation->not() : $this->expectation)->{$name}(...$arguments); $this->opposite = false; $this->shouldReset = true; return $this; } }