4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 * Marcello Duarte <marcello.duarte@gmail.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Prophecy\Call;
14 use Prophecy\Exception\Prophecy\MethodProphecyException;
15 use Prophecy\Prophecy\MethodProphecy;
16 use Prophecy\Prophecy\ObjectProphecy;
17 use Prophecy\Argument\ArgumentsWildcard;
18 use Prophecy\Util\StringUtil;
19 use Prophecy\Exception\Call\UnexpectedCallException;
22 * Calls receiver & manager.
24 * @author Konstantin Kudryashov <ever.zet@gmail.com>
33 private $recordedCalls = array();
36 * Initializes call center.
38 * @param StringUtil $util
40 public function __construct(StringUtil $util = null)
42 $this->util = $util ?: new StringUtil;
46 * Makes and records specific method call for object prophecy.
48 * @param ObjectProphecy $prophecy
49 * @param string $methodName
50 * @param array $arguments
52 * @return mixed Returns null if no promise for prophecy found or promise return value.
54 * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found
56 public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments)
58 // For efficiency exclude 'args' from the generated backtrace
59 if (PHP_VERSION_ID >= 50400) {
60 // Limit backtrace to last 3 calls as we don't use the rest
61 // Limit argument was introduced in PHP 5.4.0
62 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
63 } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
64 // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6
65 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
67 $backtrace = debug_backtrace();
71 if (isset($backtrace[2]) && isset($backtrace[2]['file'])) {
72 $file = $backtrace[2]['file'];
73 $line = $backtrace[2]['line'];
76 // If no method prophecies defined, then it's a dummy, so we'll just return null
77 if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) {
78 $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line);
83 // There are method prophecies, so it's a fake/stub. Searching prophecy for this call
85 foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) {
86 if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) {
87 $matches[] = array($score, $methodProphecy);
91 // If fake/stub doesn't have method prophecy for this call - throw exception
92 if (!count($matches)) {
93 throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments);
96 // Sort matches by their score value
97 @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; });
99 // If Highest rated method prophecy has a promise - execute it or return null instead
100 $methodProphecy = $matches[0][1];
103 if ($promise = $methodProphecy->getPromise()) {
105 $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy);
106 } catch (\Exception $e) {
111 if ($methodProphecy->hasReturnVoid() && $returnValue !== null) {
112 throw new MethodProphecyException(
113 "The method \"$methodName\" has a void return type, but the promise returned a value",
118 $this->recordedCalls[] = new Call(
119 $methodName, $arguments, $returnValue, $exception, $file, $line
122 if (null !== $exception) {
130 * Searches for calls by method name & arguments wildcard.
132 * @param string $methodName
133 * @param ArgumentsWildcard $wildcard
137 public function findCalls($methodName, ArgumentsWildcard $wildcard)
140 array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) {
141 return $methodName === $call->getMethodName()
142 && 0 < $wildcard->scoreArguments($call->getArguments())
148 private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName,
151 $classname = get_class($prophecy->reveal());
152 $argstring = implode(', ', array_map(array($this->util, 'stringify'), $arguments));
153 $expected = implode("\n", array_map(function (MethodProphecy $methodProphecy) {
154 return sprintf(' - %s(%s)',
155 $methodProphecy->getMethodName(),
156 $methodProphecy->getArgumentsWildcard()
158 }, call_user_func_array('array_merge', $prophecy->getMethodProphecies())));
160 return new UnexpectedCallException(
164 "on %s was not expected, expected calls were:\n%s",
166 $methodName, $argstring, $classname, $expected
168 $prophecy, $methodName, $arguments