PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ThrowMatcher.php

https://gitlab.com/Laolballs/evotting
PHP | 271 lines | 160 code | 32 blank | 79 comment | 20 complexity | 0ddcc03e34c1b398fa6e3396ef79b4f6 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of PhpSpec, A php toolset to drive emergent
  4. * design by specification.
  5. *
  6. * (c) Marcello Duarte <marcello.duarte@gmail.com>
  7. * (c) Konstantin Kudryashov <ever.zet@gmail.com>
  8. *
  9. * For the full copyright and license information, please view the LICENSE
  10. * file that was distributed with this source code.
  11. */
  12. namespace PhpSpec\Matcher;
  13. use PhpSpec\Formatter\Presenter\PresenterInterface;
  14. use PhpSpec\Wrapper\Unwrapper;
  15. use PhpSpec\Wrapper\DelayedCall;
  16. use PhpSpec\Factory\ReflectionFactory;
  17. use PhpSpec\Exception\Example\MatcherException;
  18. use PhpSpec\Exception\Example\FailureException;
  19. use PhpSpec\Exception\Example\NotEqualException;
  20. use PhpSpec\Exception\Fracture\MethodNotFoundException;
  21. class ThrowMatcher implements MatcherInterface
  22. {
  23. /**
  24. * @var array
  25. */
  26. private static $ignoredProperties = array('file', 'line', 'string', 'trace', 'previous');
  27. /**
  28. * @var \PhpSpec\Wrapper\Unwrapper
  29. */
  30. private $unwrapper;
  31. /**
  32. * @var \PhpSpec\Formatter\Presenter\PresenterInterface
  33. */
  34. private $presenter;
  35. /**
  36. * @var ReflectionFactory
  37. */
  38. private $factory;
  39. /**
  40. * @param Unwrapper $unwrapper
  41. * @param PresenterInterface $presenter
  42. * @param ReflectionFactory $factory
  43. */
  44. public function __construct(Unwrapper $unwrapper, PresenterInterface $presenter, ReflectionFactory $factory = null)
  45. {
  46. $this->unwrapper = $unwrapper;
  47. $this->presenter = $presenter;
  48. $this->factory = $factory ?: new ReflectionFactory();
  49. }
  50. /**
  51. * @param string $name
  52. * @param mixed $subject
  53. * @param array $arguments
  54. *
  55. * @return bool
  56. */
  57. public function supports($name, $subject, array $arguments)
  58. {
  59. return 'throw' === $name;
  60. }
  61. /**
  62. * @param string $name
  63. * @param mixed $subject
  64. * @param array $arguments
  65. *
  66. * @return DelayedCall
  67. */
  68. public function positiveMatch($name, $subject, array $arguments)
  69. {
  70. return $this->getDelayedCall(array($this, 'verifyPositive'), $subject, $arguments);
  71. }
  72. /**
  73. * @param string $name
  74. * @param mixed $subject
  75. * @param array $arguments
  76. *
  77. * @return DelayedCall
  78. */
  79. public function negativeMatch($name, $subject, array $arguments)
  80. {
  81. return $this->getDelayedCall(array($this, 'verifyNegative'), $subject, $arguments);
  82. }
  83. /**
  84. * @param callable $callable
  85. * @param array $arguments
  86. * @param null $exception
  87. *
  88. * @throws \PhpSpec\Exception\Example\FailureException
  89. * @throws \PhpSpec\Exception\Example\NotEqualException
  90. */
  91. public function verifyPositive($callable, array $arguments, $exception = null)
  92. {
  93. try {
  94. call_user_func_array($callable, $arguments);
  95. } catch (\Exception $e) {
  96. if (null === $exception) {
  97. return;
  98. }
  99. if (!$e instanceof $exception) {
  100. throw new FailureException(sprintf(
  101. 'Expected exception of class %s, but got %s.',
  102. $this->presenter->presentValue($exception),
  103. $this->presenter->presentValue($e)
  104. ));
  105. }
  106. if (is_object($exception)) {
  107. $exceptionRefl = $this->factory->create($exception);
  108. foreach ($exceptionRefl->getProperties() as $property) {
  109. if (in_array($property->getName(), self::$ignoredProperties)) {
  110. continue;
  111. }
  112. $property->setAccessible(true);
  113. $expected = $property->getValue($exception);
  114. $actual = $property->getValue($e);
  115. if (null !== $expected && $actual !== $expected) {
  116. throw new NotEqualException(sprintf(
  117. 'Expected exception `%s` to be %s, but it is %s.',
  118. $property->getName(),
  119. $this->presenter->presentValue($expected),
  120. $this->presenter->presentValue($actual)
  121. ), $expected, $actual);
  122. }
  123. }
  124. }
  125. return;
  126. }
  127. throw new FailureException('Expected to get exception, none got.');
  128. }
  129. /**
  130. * @param callable $callable
  131. * @param array $arguments
  132. * @param string|null $exception
  133. *
  134. * @throws \PhpSpec\Exception\Example\FailureException
  135. */
  136. public function verifyNegative($callable, array $arguments, $exception = null)
  137. {
  138. try {
  139. call_user_func_array($callable, $arguments);
  140. } catch (\Exception $e) {
  141. if (null === $exception) {
  142. throw new FailureException(sprintf(
  143. 'Expected to not throw any exceptions, but got %s.',
  144. $this->presenter->presentValue($e)
  145. ));
  146. }
  147. if ($e instanceof $exception) {
  148. $invalidProperties = array();
  149. if (is_object($exception)) {
  150. $exceptionRefl = $this->factory->create($exception);
  151. foreach ($exceptionRefl->getProperties() as $property) {
  152. if (in_array($property->getName(), self::$ignoredProperties)) {
  153. continue;
  154. }
  155. $property->setAccessible(true);
  156. $expected = $property->getValue($exception);
  157. $actual = $property->getValue($e);
  158. if (null !== $expected && $actual === $expected) {
  159. $invalidProperties[] = sprintf(' `%s`=%s',
  160. $property->getName(),
  161. $this->presenter->presentValue($expected)
  162. );
  163. }
  164. }
  165. }
  166. $withProperties = '';
  167. if (count($invalidProperties) > 0) {
  168. $withProperties = sprintf(' with'.PHP_EOL.'%s,'.PHP_EOL,
  169. implode(",\n", $invalidProperties)
  170. );
  171. }
  172. throw new FailureException(sprintf(
  173. 'Expected to not throw %s exception%s but got it.',
  174. $this->presenter->presentValue($exception),
  175. $withProperties
  176. ));
  177. }
  178. }
  179. }
  180. /**
  181. * @return int
  182. */
  183. public function getPriority()
  184. {
  185. return 1;
  186. }
  187. /**
  188. * @param callable $check
  189. * @param mixed $subject
  190. * @param array $arguments
  191. *
  192. * @return DelayedCall
  193. */
  194. private function getDelayedCall($check, $subject, array $arguments)
  195. {
  196. $exception = $this->getException($arguments);
  197. $unwrapper = $this->unwrapper;
  198. return new DelayedCall(
  199. function ($method, $arguments) use ($check, $subject, $exception, $unwrapper) {
  200. $arguments = $unwrapper->unwrapAll($arguments);
  201. $methodName = $arguments[0];
  202. $arguments = isset($arguments[1]) ? $arguments[1] : array();
  203. $callable = array($subject, $methodName);
  204. list($class, $methodName) = array($subject, $methodName);
  205. if (!method_exists($class, $methodName) && !method_exists($class, '__call')) {
  206. throw new MethodNotFoundException(
  207. sprintf('Method %s::%s not found.', get_class($class), $methodName),
  208. $class, $methodName, $arguments
  209. );
  210. }
  211. return call_user_func($check, $callable, $arguments, $exception);
  212. }
  213. );
  214. }
  215. /**
  216. * @param array $arguments
  217. *
  218. * @return null|string
  219. * @throws \PhpSpec\Exception\Example\MatcherException
  220. */
  221. private function getException(array $arguments)
  222. {
  223. if (0 == count($arguments)) {
  224. return null;
  225. }
  226. if (is_string($arguments[0])) {
  227. return $arguments[0];
  228. }
  229. if (is_object($arguments[0]) && $arguments[0] instanceof \Exception) {
  230. return $arguments[0];
  231. }
  232. throw new MatcherException(sprintf(
  233. "Wrong argument provided in throw matcher.\n".
  234. "Fully qualified classname or exception instance expected,\n".
  235. "Got %s.",
  236. $this->presenter->presentValue($arguments[0])
  237. ));
  238. }
  239. }