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\Prophecy;
14 use SebastianBergmann\Comparator\ComparisonFailure;
15 use Prophecy\Comparator\Factory as ComparatorFactory;
16 use Prophecy\Call\Call;
17 use Prophecy\Doubler\LazyDouble;
18 use Prophecy\Argument\ArgumentsWildcard;
19 use Prophecy\Call\CallCenter;
20 use Prophecy\Exception\Prophecy\ObjectProphecyException;
21 use Prophecy\Exception\Prophecy\MethodProphecyException;
22 use Prophecy\Exception\Prediction\AggregateException;
23 use Prophecy\Exception\Prediction\PredictionException;
28 * @author Konstantin Kudryashov <ever.zet@gmail.com>
30 class ObjectProphecy implements ProphecyInterface
35 private $comparatorFactory;
38 * @var MethodProphecy[][]
40 private $methodProphecies = array();
43 * Initializes object prophecy.
45 * @param LazyDouble $lazyDouble
46 * @param CallCenter $callCenter
47 * @param RevealerInterface $revealer
48 * @param ComparatorFactory $comparatorFactory
50 public function __construct(
51 LazyDouble $lazyDouble,
52 CallCenter $callCenter = null,
53 RevealerInterface $revealer = null,
54 ComparatorFactory $comparatorFactory = null
56 $this->lazyDouble = $lazyDouble;
57 $this->callCenter = $callCenter ?: new CallCenter;
58 $this->revealer = $revealer ?: new Revealer;
60 $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
64 * Forces double to extend specific class.
66 * @param string $class
70 public function willExtend($class)
72 $this->lazyDouble->setParentClass($class);
78 * Forces double to implement specific interface.
80 * @param string $interface
84 public function willImplement($interface)
86 $this->lazyDouble->addInterface($interface);
92 * Sets constructor arguments.
94 * @param array $arguments
98 public function willBeConstructedWith(array $arguments = null)
100 $this->lazyDouble->setArguments($arguments);
110 * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface
112 public function reveal()
114 $double = $this->lazyDouble->getInstance();
116 if (null === $double || !$double instanceof ProphecySubjectInterface) {
117 throw new ObjectProphecyException(
118 "Generated double must implement ProphecySubjectInterface, but it does not.\n".
119 'It seems you have wrongly configured doubler without required ClassPatch.',
124 $double->setProphecy($this);
130 * Adds method prophecy to object prophecy.
132 * @param MethodProphecy $methodProphecy
134 * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't
135 * have arguments wildcard
137 public function addMethodProphecy(MethodProphecy $methodProphecy)
139 $argumentsWildcard = $methodProphecy->getArgumentsWildcard();
140 if (null === $argumentsWildcard) {
141 throw new MethodProphecyException(sprintf(
142 "Can not add prophecy for a method `%s::%s()`\n".
143 "as you did not specify arguments wildcard for it.",
144 get_class($this->reveal()),
145 $methodProphecy->getMethodName()
149 $methodName = $methodProphecy->getMethodName();
151 if (!isset($this->methodProphecies[$methodName])) {
152 $this->methodProphecies[$methodName] = array();
155 $this->methodProphecies[$methodName][] = $methodProphecy;
159 * Returns either all or related to single method prophecies.
161 * @param null|string $methodName
163 * @return MethodProphecy[]
165 public function getMethodProphecies($methodName = null)
167 if (null === $methodName) {
168 return $this->methodProphecies;
171 if (!isset($this->methodProphecies[$methodName])) {
175 return $this->methodProphecies[$methodName];
179 * Makes specific method call.
181 * @param string $methodName
182 * @param array $arguments
186 public function makeProphecyMethodCall($methodName, array $arguments)
188 $arguments = $this->revealer->reveal($arguments);
189 $return = $this->callCenter->makeCall($this, $methodName, $arguments);
191 return $this->revealer->reveal($return);
195 * Finds calls by method name & arguments wildcard.
197 * @param string $methodName
198 * @param ArgumentsWildcard $wildcard
202 public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard)
204 return $this->callCenter->findCalls($methodName, $wildcard);
208 * Checks that registered method predictions do not fail.
210 * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail
212 public function checkProphecyMethodsPredictions()
214 $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal())));
215 $exception->setObjectProphecy($this);
217 foreach ($this->methodProphecies as $prophecies) {
218 foreach ($prophecies as $prophecy) {
220 $prophecy->checkPrediction();
221 } catch (PredictionException $e) {
222 $exception->append($e);
227 if (count($exception->getExceptions())) {
233 * Creates new method prophecy using specified method name and arguments.
235 * @param string $methodName
236 * @param array $arguments
238 * @return MethodProphecy
240 public function __call($methodName, array $arguments)
242 $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments));
244 foreach ($this->getMethodProphecies($methodName) as $prophecy) {
245 $argumentsWildcard = $prophecy->getArgumentsWildcard();
246 $comparator = $this->comparatorFactory->getComparatorFor(
247 $argumentsWildcard, $arguments
251 $comparator->assertEquals($argumentsWildcard, $arguments);
253 } catch (ComparisonFailure $failure) {}
256 return new MethodProphecy($this, $methodName, $arguments);
260 * Tries to get property value from double.
262 * @param string $name
266 public function __get($name)
268 return $this->reveal()->$name;
272 * Tries to set property value to double.
274 * @param string $name
275 * @param mixed $value
277 public function __set($name, $value)
279 $this->reveal()->$name = $this->revealer->reveal($value);