InvocationMocker.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. /*
  3. * This file is part of PHPUnit.
  4. *
  5. * This file is modified to replace the Match interface with ParametersMatch,
  6. * to avoid parse errors due to `match` being a reserved keyword in PHP 8.
  7. *
  8. * When the test suite is updated for compatibility with PHPUnit 9.x,
  9. * this override can be removed.
  10. *
  11. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  12. *
  13. * For the full copyright and license information, please view the LICENSE
  14. * file that was distributed with this source code.
  15. */
  16. namespace PHPUnit\Framework\MockObject;
  17. use Exception;
  18. use PHPUnit\Framework\ExpectationFailedException;
  19. use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
  20. use PHPUnit\Framework\MockObject\Builder\ParametersMatch;
  21. use PHPUnit\Framework\MockObject\Builder\NamespaceMatch;
  22. use PHPUnit\Framework\MockObject\Matcher\DeferredError;
  23. use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation;
  24. use PHPUnit\Framework\MockObject\Stub\MatcherCollection;
  25. /**
  26. * Mocker for invocations which are sent from
  27. * MockObject objects.
  28. *
  29. * Keeps track of all expectations and stubs as well as registering
  30. * identifications for builders.
  31. */
  32. class InvocationMocker implements Invokable, MatcherCollection, NamespaceMatch
  33. {
  34. /**
  35. * @var MatcherInvocation[]
  36. */
  37. private $matchers = [];
  38. /**
  39. * @var Match[]
  40. */
  41. private $builderMap = [];
  42. /**
  43. * @var string[]
  44. */
  45. private $configurableMethods;
  46. /**
  47. * @var bool
  48. */
  49. private $returnValueGeneration;
  50. public function __construct(array $configurableMethods, bool $returnValueGeneration)
  51. {
  52. $this->configurableMethods = $configurableMethods;
  53. $this->returnValueGeneration = $returnValueGeneration;
  54. }
  55. public function addMatcher(MatcherInvocation $matcher): void
  56. {
  57. $this->matchers[] = $matcher;
  58. }
  59. public function hasMatchers()
  60. {
  61. foreach ($this->matchers as $matcher) {
  62. if ($matcher->hasMatchers()) {
  63. return true;
  64. }
  65. }
  66. return false;
  67. }
  68. /**
  69. * @return null|bool
  70. */
  71. public function lookupId($id)
  72. {
  73. if (isset($this->builderMap[$id])) {
  74. return $this->builderMap[$id];
  75. }
  76. }
  77. /**
  78. * @throws RuntimeException
  79. */
  80. public function registerId($id, ParametersMatch $builder): void
  81. {
  82. if (isset($this->builderMap[$id])) {
  83. throw new RuntimeException(
  84. 'Match builder with id <' . $id . '> is already registered.'
  85. );
  86. }
  87. $this->builderMap[$id] = $builder;
  88. }
  89. /**
  90. * @return BuilderInvocationMocker
  91. */
  92. public function expects(MatcherInvocation $matcher)
  93. {
  94. return new BuilderInvocationMocker(
  95. $this,
  96. $matcher,
  97. $this->configurableMethods
  98. );
  99. }
  100. /**
  101. * @throws Exception
  102. */
  103. public function invoke(Invocation $invocation)
  104. {
  105. $exception = null;
  106. $hasReturnValue = false;
  107. $returnValue = null;
  108. foreach ($this->matchers as $match) {
  109. try {
  110. if ($match->matches($invocation)) {
  111. $value = $match->invoked($invocation);
  112. if (!$hasReturnValue) {
  113. $returnValue = $value;
  114. $hasReturnValue = true;
  115. }
  116. }
  117. } catch (Exception $e) {
  118. $exception = $e;
  119. }
  120. }
  121. if ($exception !== null) {
  122. throw $exception;
  123. }
  124. if ($hasReturnValue) {
  125. return $returnValue;
  126. }
  127. if ($this->returnValueGeneration === false) {
  128. $exception = new ExpectationFailedException(
  129. \sprintf(
  130. 'Return value inference disabled and no expectation set up for %s::%s()',
  131. $invocation->getClassName(),
  132. $invocation->getMethodName()
  133. )
  134. );
  135. if (\strtolower($invocation->getMethodName()) === '__tostring') {
  136. $this->addMatcher(new DeferredError($exception));
  137. return '';
  138. }
  139. throw $exception;
  140. }
  141. return $invocation->generateReturnValue();
  142. }
  143. /**
  144. * @return bool
  145. */
  146. public function matches(Invocation $invocation)
  147. {
  148. foreach ($this->matchers as $matcher) {
  149. if (!$matcher->matches($invocation)) {
  150. return false;
  151. }
  152. }
  153. return true;
  154. }
  155. /**
  156. * @throws \PHPUnit\Framework\ExpectationFailedException
  157. *
  158. * @return bool
  159. */
  160. public function verify()
  161. {
  162. foreach ($this->matchers as $matcher) {
  163. $matcher->verify();
  164. }
  165. }
  166. }