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