1<?php
2
3/*
4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 *     Marcello Duarte <marcello.duarte@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Prophecy;
13
14use Prophecy\Doubler\Doubler;
15use Prophecy\Doubler\LazyDouble;
16use Prophecy\Doubler\ClassPatch;
17use Prophecy\Prophecy\ObjectProphecy;
18use Prophecy\Prophecy\RevealerInterface;
19use Prophecy\Prophecy\Revealer;
20use Prophecy\Call\CallCenter;
21use Prophecy\Util\StringUtil;
22use Prophecy\Exception\Prediction\PredictionException;
23use Prophecy\Exception\Prediction\AggregateException;
24
25/**
26 * Prophet creates prophecies.
27 *
28 * @author Konstantin Kudryashov <ever.zet@gmail.com>
29 */
30class Prophet
31{
32    private $doubler;
33    private $revealer;
34    private $util;
35
36    /**
37     * @var ObjectProphecy[]
38     */
39    private $prophecies = array();
40
41    /**
42     * Initializes Prophet.
43     *
44     * @param null|Doubler           $doubler
45     * @param null|RevealerInterface $revealer
46     * @param null|StringUtil        $util
47     */
48    public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null,
49                                StringUtil $util = null)
50    {
51        if (null === $doubler) {
52            $doubler = new Doubler;
53            $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch);
54            $doubler->registerClassPatch(new ClassPatch\TraversablePatch);
55            $doubler->registerClassPatch(new ClassPatch\ThrowablePatch);
56            $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch);
57            $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch);
58            $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch);
59            $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch());
60            $doubler->registerClassPatch(new ClassPatch\MagicCallPatch);
61            $doubler->registerClassPatch(new ClassPatch\KeywordPatch);
62        }
63
64        $this->doubler  = $doubler;
65        $this->revealer = $revealer ?: new Revealer;
66        $this->util     = $util ?: new StringUtil;
67    }
68
69    /**
70     * Creates new object prophecy.
71     *
72     * @param null|string $classOrInterface Class or interface name
73     *
74     * @return ObjectProphecy
75     */
76    public function prophesize($classOrInterface = null)
77    {
78        $this->prophecies[] = $prophecy = new ObjectProphecy(
79            new LazyDouble($this->doubler),
80            new CallCenter($this->util),
81            $this->revealer
82        );
83
84        if ($classOrInterface && class_exists($classOrInterface)) {
85            return $prophecy->willExtend($classOrInterface);
86        }
87
88        if ($classOrInterface && interface_exists($classOrInterface)) {
89            return $prophecy->willImplement($classOrInterface);
90        }
91
92        return $prophecy;
93    }
94
95    /**
96     * Returns all created object prophecies.
97     *
98     * @return ObjectProphecy[]
99     */
100    public function getProphecies()
101    {
102        return $this->prophecies;
103    }
104
105    /**
106     * Returns Doubler instance assigned to this Prophet.
107     *
108     * @return Doubler
109     */
110    public function getDoubler()
111    {
112        return $this->doubler;
113    }
114
115    /**
116     * Checks all predictions defined by prophecies of this Prophet.
117     *
118     * @throws Exception\Prediction\AggregateException If any prediction fails
119     */
120    public function checkPredictions()
121    {
122        $exception = new AggregateException("Some predictions failed:\n");
123        foreach ($this->prophecies as $prophecy) {
124            try {
125                $prophecy->checkProphecyMethodsPredictions();
126            } catch (PredictionException $e) {
127                $exception->append($e);
128            }
129        }
130
131        if (count($exception->getExceptions())) {
132            throw $exception;
133        }
134    }
135}
136