Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: namespace PHPUnit\Framework\MockObject; Chris@14: Chris@14: use PHPUnit\Framework\ExpectationFailedException; Chris@14: use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount; Chris@14: use PHPUnit\Framework\MockObject\Matcher\AnyParameters; Chris@14: use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation; Chris@14: use PHPUnit\Framework\MockObject\Matcher\InvokedCount; Chris@14: use PHPUnit\Framework\MockObject\Matcher\MethodName; Chris@14: use PHPUnit\Framework\MockObject\Matcher\Parameters; Chris@14: use PHPUnit\Framework\TestFailure; Chris@14: Chris@14: /** Chris@14: * Main matcher which defines a full expectation using method, parameter and Chris@14: * invocation matchers. Chris@14: * This matcher encapsulates all the other matchers and allows the builder to Chris@14: * set the specific matchers when the appropriate methods are called (once(), Chris@14: * where() etc.). Chris@14: * Chris@14: * All properties are public so that they can easily be accessed by the builder. Chris@14: */ Chris@14: class Matcher implements MatcherInvocation Chris@14: { Chris@14: /** Chris@14: * @var MatcherInvocation Chris@14: */ Chris@14: private $invocationMatcher; Chris@14: Chris@14: /** Chris@14: * @var mixed Chris@14: */ Chris@14: private $afterMatchBuilderId = null; Chris@14: Chris@14: /** Chris@14: * @var bool Chris@14: */ Chris@14: private $afterMatchBuilderIsInvoked = false; Chris@14: Chris@14: /** Chris@14: * @var MethodName Chris@14: */ Chris@14: private $methodNameMatcher = null; Chris@14: Chris@14: /** Chris@14: * @var Parameters Chris@14: */ Chris@14: private $parametersMatcher = null; Chris@14: Chris@14: /** Chris@14: * @var Stub Chris@14: */ Chris@14: private $stub = null; Chris@14: Chris@14: /** Chris@14: * @param MatcherInvocation $invocationMatcher Chris@14: */ Chris@14: public function __construct(MatcherInvocation $invocationMatcher) Chris@14: { Chris@14: $this->invocationMatcher = $invocationMatcher; Chris@14: } Chris@14: Chris@14: public function hasMatchers(): bool Chris@14: { Chris@14: return $this->invocationMatcher !== null && !$this->invocationMatcher instanceof AnyInvokedCount; Chris@14: } Chris@14: Chris@14: public function hasMethodNameMatcher(): bool Chris@14: { Chris@14: return $this->methodNameMatcher !== null; Chris@14: } Chris@14: Chris@14: public function getMethodNameMatcher(): MethodName Chris@14: { Chris@14: return $this->methodNameMatcher; Chris@14: } Chris@14: Chris@14: public function setMethodNameMatcher(MethodName $matcher) Chris@14: { Chris@14: $this->methodNameMatcher = $matcher; Chris@14: } Chris@14: Chris@14: public function hasParametersMatcher(): bool Chris@14: { Chris@14: return $this->parametersMatcher !== null; Chris@14: } Chris@14: Chris@14: public function getParametersMatcher(): Parameters Chris@14: { Chris@14: return $this->parametersMatcher; Chris@14: } Chris@14: Chris@14: public function setParametersMatcher($matcher) Chris@14: { Chris@14: $this->parametersMatcher = $matcher; Chris@14: } Chris@14: Chris@14: public function setStub($stub) Chris@14: { Chris@14: $this->stub = $stub; Chris@14: } Chris@14: Chris@14: public function setAfterMatchBuilderId($id) Chris@14: { Chris@14: $this->afterMatchBuilderId = $id; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Invocation $invocation Chris@14: * Chris@14: * @return mixed Chris@14: * Chris@14: * @throws \Exception Chris@14: * @throws RuntimeException Chris@14: * @throws ExpectationFailedException Chris@14: */ Chris@14: public function invoked(Invocation $invocation) Chris@14: { Chris@14: if ($this->invocationMatcher === null) { Chris@14: throw new RuntimeException( Chris@14: 'No invocation matcher is set' Chris@14: ); Chris@14: } Chris@14: Chris@14: if ($this->methodNameMatcher === null) { Chris@14: throw new RuntimeException('No method matcher is set'); Chris@14: } Chris@14: Chris@14: if ($this->afterMatchBuilderId !== null) { Chris@14: $builder = $invocation->getObject() Chris@14: ->__phpunit_getInvocationMocker() Chris@14: ->lookupId($this->afterMatchBuilderId); Chris@14: Chris@14: if (!$builder) { Chris@14: throw new RuntimeException( Chris@14: \sprintf( Chris@14: 'No builder found for match builder identification <%s>', Chris@14: $this->afterMatchBuilderId Chris@14: ) Chris@14: ); Chris@14: } Chris@14: Chris@14: $matcher = $builder->getMatcher(); Chris@14: Chris@14: if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) { Chris@14: $this->afterMatchBuilderIsInvoked = true; Chris@14: } Chris@14: } Chris@14: Chris@14: $this->invocationMatcher->invoked($invocation); Chris@14: Chris@14: try { Chris@14: if ($this->parametersMatcher !== null && Chris@14: !$this->parametersMatcher->matches($invocation)) { Chris@14: $this->parametersMatcher->verify(); Chris@14: } Chris@14: } catch (ExpectationFailedException $e) { Chris@14: throw new ExpectationFailedException( Chris@14: \sprintf( Chris@14: "Expectation failed for %s when %s\n%s", Chris@14: $this->methodNameMatcher->toString(), Chris@14: $this->invocationMatcher->toString(), Chris@14: $e->getMessage() Chris@14: ), Chris@14: $e->getComparisonFailure() Chris@14: ); Chris@14: } Chris@14: Chris@14: if ($this->stub) { Chris@14: return $this->stub->invoke($invocation); Chris@14: } Chris@14: Chris@14: return $invocation->generateReturnValue(); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Invocation $invocation Chris@14: * Chris@14: * @return bool Chris@14: * Chris@14: * @throws RuntimeException Chris@14: * @throws ExpectationFailedException Chris@14: */ Chris@14: public function matches(Invocation $invocation) Chris@14: { Chris@14: if ($this->afterMatchBuilderId !== null) { Chris@14: $builder = $invocation->getObject() Chris@14: ->__phpunit_getInvocationMocker() Chris@14: ->lookupId($this->afterMatchBuilderId); Chris@14: Chris@14: if (!$builder) { Chris@14: throw new RuntimeException( Chris@14: \sprintf( Chris@14: 'No builder found for match builder identification <%s>', Chris@14: $this->afterMatchBuilderId Chris@14: ) Chris@14: ); Chris@14: } Chris@14: Chris@14: $matcher = $builder->getMatcher(); Chris@14: Chris@14: if (!$matcher) { Chris@14: return false; Chris@14: } Chris@14: Chris@14: if (!$matcher->invocationMatcher->hasBeenInvoked()) { Chris@14: return false; Chris@14: } Chris@14: } Chris@14: Chris@14: if ($this->invocationMatcher === null) { Chris@14: throw new RuntimeException( Chris@14: 'No invocation matcher is set' Chris@14: ); Chris@14: } Chris@14: Chris@14: if ($this->methodNameMatcher === null) { Chris@14: throw new RuntimeException('No method matcher is set'); Chris@14: } Chris@14: Chris@14: if (!$this->invocationMatcher->matches($invocation)) { Chris@14: return false; Chris@14: } Chris@14: Chris@14: try { Chris@14: if (!$this->methodNameMatcher->matches($invocation)) { Chris@14: return false; Chris@14: } Chris@14: } catch (ExpectationFailedException $e) { Chris@14: throw new ExpectationFailedException( Chris@14: \sprintf( Chris@14: "Expectation failed for %s when %s\n%s", Chris@14: $this->methodNameMatcher->toString(), Chris@14: $this->invocationMatcher->toString(), Chris@14: $e->getMessage() Chris@14: ), Chris@14: $e->getComparisonFailure() Chris@14: ); Chris@14: } Chris@14: Chris@14: return true; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @throws RuntimeException Chris@14: * @throws ExpectationFailedException Chris@14: */ Chris@14: public function verify() Chris@14: { Chris@14: if ($this->invocationMatcher === null) { Chris@14: throw new RuntimeException( Chris@14: 'No invocation matcher is set' Chris@14: ); Chris@14: } Chris@14: Chris@14: if ($this->methodNameMatcher === null) { Chris@14: throw new RuntimeException('No method matcher is set'); Chris@14: } Chris@14: Chris@14: try { Chris@14: $this->invocationMatcher->verify(); Chris@14: Chris@14: if ($this->parametersMatcher === null) { Chris@14: $this->parametersMatcher = new AnyParameters; Chris@14: } Chris@14: Chris@14: $invocationIsAny = $this->invocationMatcher instanceof AnyInvokedCount; Chris@14: $invocationIsNever = $this->invocationMatcher instanceof InvokedCount && $this->invocationMatcher->isNever(); Chris@14: Chris@14: if (!$invocationIsAny && !$invocationIsNever) { Chris@14: $this->parametersMatcher->verify(); Chris@14: } Chris@14: } catch (ExpectationFailedException $e) { Chris@14: throw new ExpectationFailedException( Chris@14: \sprintf( Chris@14: "Expectation failed for %s when %s.\n%s", Chris@14: $this->methodNameMatcher->toString(), Chris@14: $this->invocationMatcher->toString(), Chris@14: TestFailure::exceptionToString($e) Chris@14: ) Chris@14: ); Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * @return string Chris@14: */ Chris@14: public function toString() Chris@14: { Chris@14: $list = []; Chris@14: Chris@14: if ($this->invocationMatcher !== null) { Chris@14: $list[] = $this->invocationMatcher->toString(); Chris@14: } Chris@14: Chris@14: if ($this->methodNameMatcher !== null) { Chris@14: $list[] = 'where ' . $this->methodNameMatcher->toString(); Chris@14: } Chris@14: Chris@14: if ($this->parametersMatcher !== null) { Chris@14: $list[] = 'and ' . $this->parametersMatcher->toString(); Chris@14: } Chris@14: Chris@14: if ($this->afterMatchBuilderId !== null) { Chris@14: $list[] = 'after ' . $this->afterMatchBuilderId; Chris@14: } Chris@14: Chris@14: if ($this->stub !== null) { Chris@14: $list[] = 'will ' . $this->stub->toString(); Chris@14: } Chris@14: Chris@14: return \implode(' ', $list); Chris@14: } Chris@14: }