PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/StringPresenter.php

https://gitlab.com/Pasantias/pasantiasASLG
PHP | 492 lines | 279 code | 71 blank | 142 comment | 39 complexity | 9cfabe0b0756076a45f8f78e4597ace3 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\Formatter\Presenter;
  13. use Exception;
  14. use PhpSpec\Exception\Exception as PhpSpecException;
  15. use PhpSpec\Exception\Example\NotEqualException;
  16. use PhpSpec\Exception\Example\ErrorException;
  17. use PhpSpec\Exception\Example\PendingException;
  18. use Prophecy\Argument\Token\ExactValueToken;
  19. use Prophecy\Argument\Token\TokenInterface;
  20. use Prophecy\Exception\Call\UnexpectedCallException;
  21. use Prophecy\Exception\Exception as ProphecyException;
  22. use Prophecy\Prophecy\MethodProphecy;
  23. /**
  24. * @deprecated Use PhpSpec\Formatter\Presenter\SimplePresenter instead
  25. */
  26. class StringPresenter implements PresenterInterface
  27. {
  28. /**
  29. * @var Differ\Differ
  30. */
  31. private $differ;
  32. /**
  33. * The PhpSpec base path.
  34. *
  35. * This property is used as a constant but PHP does not support setting the value with dirname().
  36. *
  37. * @var string
  38. */
  39. private $phpspecPath;
  40. /**
  41. * The PhpSpec Runner base path.
  42. *
  43. * This property is used as a constant but PHP does not support setting the value with dirname().
  44. *
  45. * @var string
  46. */
  47. private $runnerPath;
  48. /**
  49. * @param Differ\Differ $differ
  50. */
  51. public function __construct(Differ\Differ $differ)
  52. {
  53. $this->differ = $differ;
  54. $this->phpspecPath = dirname(dirname(__DIR__));
  55. $this->runnerPath = $this->phpspecPath.DIRECTORY_SEPARATOR.'Runner';
  56. }
  57. /**
  58. * @param mixed $value
  59. *
  60. * @return string
  61. */
  62. public function presentValue($value)
  63. {
  64. if (is_callable($value)) {
  65. return $this->presentString($this->presentCallable($value));
  66. }
  67. if ($value instanceof Exception) {
  68. return $this->presentString(sprintf(
  69. '[exc:%s("%s")]',
  70. get_class($value),
  71. $value->getMessage()
  72. ));
  73. }
  74. switch ($type = strtolower(gettype($value))) {
  75. case 'null':
  76. return $this->presentString('null');
  77. case 'boolean':
  78. return $this->presentString(sprintf('%s', true === $value ? 'true' : 'false'));
  79. case 'object':
  80. return $this->presentString(sprintf('[obj:%s]', get_class($value)));
  81. case 'array':
  82. return $this->presentString(sprintf('[array:%d]', count($value)));
  83. case 'string':
  84. if (25 > strlen($value) && false === strpos($value, "\n")) {
  85. return $this->presentString(sprintf('"%s"', $value));
  86. }
  87. $lines = explode("\n", $value);
  88. return $this->presentString(sprintf('"%s..."', substr($lines[0], 0, 25)));
  89. default:
  90. return $this->presentString(sprintf('[%s:%s]', $type, $value));
  91. }
  92. }
  93. /**
  94. * @param Exception $exception
  95. * @param bool $verbose
  96. *
  97. * @return string
  98. */
  99. public function presentException(Exception $exception, $verbose = false)
  100. {
  101. if ($exception instanceof PhpSpecException) {
  102. $presentation = wordwrap($exception->getMessage(), 120);
  103. } elseif ($exception instanceof ProphecyException) {
  104. $presentation = $exception->getMessage();
  105. } else {
  106. $presentation = sprintf('Exception %s has been thrown.', $this->presentValue($exception));
  107. }
  108. if (!$verbose || $exception instanceof PendingException) {
  109. return $presentation;
  110. }
  111. if ($exception instanceof NotEqualException) {
  112. if ($diff = $this->presentExceptionDifference($exception)) {
  113. $presentation .= "\n".$diff;
  114. }
  115. }
  116. if ($exception instanceof PhpSpecException && !$exception instanceof ErrorException) {
  117. list($file, $line) = $this->getExceptionExamplePosition($exception);
  118. $presentation .= "\n".$this->presentFileCode($file, $line);
  119. }
  120. if ($exception instanceof UnexpectedCallException) {
  121. $presentation .= $this->presentCallArgumentsDifference($exception);
  122. }
  123. if (trim($trace = $this->presentExceptionStackTrace($exception))) {
  124. $presentation .= "\n".$trace;
  125. }
  126. return $presentation;
  127. }
  128. /**
  129. * @param string $string
  130. *
  131. * @return string
  132. */
  133. public function presentString($string)
  134. {
  135. return $string;
  136. }
  137. /**
  138. * @param string $file
  139. * @param integer $lineno
  140. * @param integer $context
  141. *
  142. * @return string
  143. */
  144. protected function presentFileCode($file, $lineno, $context = 6)
  145. {
  146. $lines = explode("\n", file_get_contents($file));
  147. $offset = max(0, $lineno - ceil($context / 2));
  148. $lines = array_slice($lines, $offset, $context);
  149. $text = "\n";
  150. foreach ($lines as $line) {
  151. $offset++;
  152. if ($offset == $lineno) {
  153. $text .= $this->presentHighlight(sprintf('%4d', $offset).' '.$line);
  154. } else {
  155. $text .= $this->presentCodeLine(sprintf('%4d', $offset), $line);
  156. }
  157. $text .= "\n";
  158. }
  159. return $text;
  160. }
  161. /**
  162. * @param integer $number
  163. * @param integer $line
  164. *
  165. * @return string
  166. */
  167. protected function presentCodeLine($number, $line)
  168. {
  169. return $number.' '.$line;
  170. }
  171. /**
  172. * @param string $line
  173. *
  174. * @return string
  175. */
  176. protected function presentHighlight($line)
  177. {
  178. return $line;
  179. }
  180. /**
  181. * @param NotEqualException $exception
  182. *
  183. * @return string
  184. */
  185. protected function presentExceptionDifference(NotEqualException $exception)
  186. {
  187. return $this->differ->compare($exception->getExpected(), $exception->getActual());
  188. }
  189. /**
  190. * @param Exception $exception
  191. *
  192. * @return string
  193. */
  194. protected function presentExceptionStackTrace(Exception $exception)
  195. {
  196. $offset = 0;
  197. $text = "\n";
  198. $text .= $this->presentExceptionTraceLocation($offset++, $exception->getFile(), $exception->getLine());
  199. $text .= $this->presentExceptionTraceFunction(
  200. 'throw new '.get_class($exception),
  201. array($exception->getMessage())
  202. );
  203. foreach ($exception->getTrace() as $call) {
  204. // skip internal framework calls
  205. if ($this->shouldStopTracePresentation($call)) {
  206. break;
  207. }
  208. if ($this->shouldSkipTracePresentation($call)) {
  209. continue;
  210. }
  211. if (isset($call['file'])) {
  212. $text .= $this->presentExceptionTraceLocation($offset++, $call['file'], $call['line']);
  213. } else {
  214. $text .= $this->presentExceptionTraceHeader(sprintf("%2d [internal]", $offset++));
  215. }
  216. if (isset($call['class'])) {
  217. $text .= $this->presentExceptionTraceMethod(
  218. $call['class'],
  219. $call['type'],
  220. $call['function'],
  221. isset($call['args']) ? $call['args'] : array()
  222. );
  223. } elseif (isset($call['function'])) {
  224. $text .= $this->presentExceptionTraceFunction(
  225. $call['function'],
  226. isset($call['args']) ? $call['args'] : array()
  227. );
  228. }
  229. }
  230. return $text;
  231. }
  232. /**
  233. * @param string $header
  234. *
  235. * @return string
  236. */
  237. protected function presentExceptionTraceHeader($header)
  238. {
  239. return $header."\n";
  240. }
  241. /**
  242. * @param string $class
  243. * @param string $type
  244. * @param string $method
  245. * @param array $args
  246. *
  247. * @return string
  248. */
  249. protected function presentExceptionTraceMethod($class, $type, $method, array $args)
  250. {
  251. $args = array_map(array($this, 'presentValue'), $args);
  252. return sprintf(" %s%s%s(%s)\n", $class, $type, $method, implode(', ', $args));
  253. }
  254. /**
  255. * @param string $function
  256. * @param array $args
  257. *
  258. * @return string
  259. */
  260. protected function presentExceptionTraceFunction($function, array $args)
  261. {
  262. $args = array_map(array($this, 'presentValue'), $args);
  263. return sprintf(" %s(%s)\n", $function, implode(', ', $args));
  264. }
  265. /**
  266. * @param PhpSpecException $exception
  267. *
  268. * @return array
  269. */
  270. protected function getExceptionExamplePosition(PhpSpecException $exception)
  271. {
  272. $refl = $exception->getCause();
  273. foreach ($exception->getTrace() as $call) {
  274. if (!isset($call['file'])) {
  275. continue;
  276. }
  277. if (!empty($refl) && $refl->getFilename() === $call['file']) {
  278. return array($call['file'], $call['line']);
  279. }
  280. }
  281. return array($exception->getFile(), $exception->getLine());
  282. }
  283. /**
  284. * @param int $offset
  285. * @param string $file
  286. * @param int $line
  287. *
  288. * @return string
  289. */
  290. private function presentExceptionTraceLocation($offset, $file, $line)
  291. {
  292. return $this->presentExceptionTraceHeader(sprintf(
  293. "%2d %s:%d",
  294. $offset,
  295. str_replace(getcwd().DIRECTORY_SEPARATOR, '', $file),
  296. $line
  297. ));
  298. }
  299. private function shouldStopTracePresentation(array $call)
  300. {
  301. return isset($call['file']) && false !== strpos($call['file'], $this->runnerPath);
  302. }
  303. private function shouldSkipTracePresentation(array $call)
  304. {
  305. if (isset($call['file']) && 0 === strpos($call['file'], $this->phpspecPath)) {
  306. return true;
  307. }
  308. return isset($call['class']) && 0 === strpos($call['class'], "PhpSpec\\");
  309. }
  310. /**
  311. * @param callable $value
  312. *
  313. * @return string
  314. */
  315. private function presentCallable($value)
  316. {
  317. if (is_array($value)) {
  318. $type = is_object($value[0]) ? $this->presentValue($value[0]) : $value[0];
  319. return sprintf('%s::%s()', $type, $value[1]);
  320. }
  321. if ($value instanceof \Closure) {
  322. return '[closure]';
  323. }
  324. if (is_object($value)) {
  325. return sprintf('[obj:%s]', get_class($value));
  326. }
  327. return sprintf('[%s()]', $value);
  328. }
  329. /**
  330. * @param UnexpectedCallException $exception
  331. *
  332. * @return string
  333. */
  334. private function presentCallArgumentsDifference(UnexpectedCallException $exception)
  335. {
  336. $actualArguments = $exception->getArguments();
  337. $methodProphecies = $exception->getObjectProphecy()->getMethodProphecies($exception->getMethodName());
  338. if ($this->noMethodPropheciesForUnexpectedCall($methodProphecies)) {
  339. return '';
  340. }
  341. $presentedMethodProphecy = $this->findMethodProphecyOfFirstNotExpectedArgumentsCall($methodProphecies, $exception);
  342. if (is_null($presentedMethodProphecy)) {
  343. return '';
  344. }
  345. $expectedTokens = $presentedMethodProphecy->getArgumentsWildcard()->getTokens();
  346. if ($this->parametersCountMismatch($expectedTokens, $actualArguments)) {
  347. return '';
  348. }
  349. $expectedArguments = $this->convertArgumentTokensToDiffableValues($expectedTokens);
  350. $text = $this->generateArgumentsDifferenceText($actualArguments, $expectedArguments);
  351. return $text;
  352. }
  353. private function noMethodPropheciesForUnexpectedCall(array $methodProphecies)
  354. {
  355. return count($methodProphecies) === 0;
  356. }
  357. /**
  358. * @param MethodProphecy[] $methodProphecies
  359. * @param UnexpectedCallException $exception
  360. *
  361. * @return MethodProphecy
  362. */
  363. private function findMethodProphecyOfFirstNotExpectedArgumentsCall(array $methodProphecies, UnexpectedCallException $exception)
  364. {
  365. $objectProphecy = $exception->getObjectProphecy();
  366. foreach ($methodProphecies as $methodProphecy) {
  367. $calls = $objectProphecy->findProphecyMethodCalls(
  368. $exception->getMethodName(),
  369. $methodProphecy->getArgumentsWildcard()
  370. );
  371. if (count($calls)) {
  372. continue;
  373. }
  374. return $methodProphecy;
  375. }
  376. }
  377. /**
  378. * @param TokenInterface[] $expectedTokens
  379. * @param array $actualArguments
  380. *
  381. * @return bool
  382. */
  383. private function parametersCountMismatch(array $expectedTokens, array $actualArguments)
  384. {
  385. return count($expectedTokens) !== count($actualArguments);
  386. }
  387. /**
  388. * @param TokenInterface[] $tokens
  389. *
  390. * @return array
  391. */
  392. private function convertArgumentTokensToDiffableValues(array $tokens)
  393. {
  394. $values = array();
  395. foreach ($tokens as $token) {
  396. if ($token instanceof ExactValueToken) {
  397. $values[] = $token->getValue();
  398. } else {
  399. $values[] = (string)$token;
  400. }
  401. }
  402. return $values;
  403. }
  404. /**
  405. * @param array $actualArguments
  406. * @param array $expectedArguments
  407. *
  408. * @return string
  409. */
  410. private function generateArgumentsDifferenceText(array $actualArguments, array $expectedArguments)
  411. {
  412. $text = '';
  413. foreach($actualArguments as $i => $actualArgument) {
  414. $expectedArgument = $expectedArguments[$i];
  415. $actualArgument = is_null($actualArgument) ? 'null' : $actualArgument;
  416. $expectedArgument = is_null($expectedArgument) ? 'null' : $expectedArgument;
  417. $text .= $this->differ->compare($expectedArgument, $actualArgument);
  418. }
  419. return $text;
  420. }
  421. }