1<?php
2/*
3 * This file is part of the PHPUnit_MockObject package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * Mocker for invocations which are sent from
13 * PHPUnit_Framework_MockObject_MockObject objects.
14 *
15 * Keeps track of all expectations and stubs as well as registering
16 * identifications for builders.
17 *
18 * @since Class available since Release 1.0.0
19 */
20class PHPUnit_Framework_MockObject_InvocationMocker implements PHPUnit_Framework_MockObject_Stub_MatcherCollection, PHPUnit_Framework_MockObject_Invokable, PHPUnit_Framework_MockObject_Builder_Namespace
21{
22    /**
23     * @var PHPUnit_Framework_MockObject_Matcher_Invocation[]
24     */
25    protected $matchers = [];
26
27    /**
28     * @var PHPUnit_Framework_MockObject_Builder_Match[]
29     */
30    protected $builderMap = [];
31
32    /**
33     * @var string[]
34     */
35    private $configurableMethods = [];
36
37    /**
38     * @param array $configurableMethods
39     */
40    public function __construct(array $configurableMethods)
41    {
42        $this->configurableMethods = $configurableMethods;
43    }
44
45    /**
46     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
47     */
48    public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
49    {
50        $this->matchers[] = $matcher;
51    }
52
53    /**
54     * @since Method available since Release 1.1.0
55     */
56    public function hasMatchers()
57    {
58        foreach ($this->matchers as $matcher) {
59            if ($matcher->hasMatchers()) {
60                return true;
61            }
62        }
63
64        return false;
65    }
66
67    /**
68     * @param mixed $id
69     *
70     * @return bool|null
71     */
72    public function lookupId($id)
73    {
74        if (isset($this->builderMap[$id])) {
75            return $this->builderMap[$id];
76        }
77
78        return;
79    }
80
81    /**
82     * @param mixed                                      $id
83     * @param PHPUnit_Framework_MockObject_Builder_Match $builder
84     *
85     * @throws PHPUnit_Framework_MockObject_RuntimeException
86     */
87    public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder)
88    {
89        if (isset($this->builderMap[$id])) {
90            throw new PHPUnit_Framework_MockObject_RuntimeException(
91                'Match builder with id <' . $id . '> is already registered.'
92            );
93        }
94
95        $this->builderMap[$id] = $builder;
96    }
97
98    /**
99     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
100     *
101     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
102     */
103    public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
104    {
105        return new PHPUnit_Framework_MockObject_Builder_InvocationMocker(
106            $this,
107            $matcher,
108            $this->configurableMethods
109        );
110    }
111
112    /**
113     * @param PHPUnit_Framework_MockObject_Invocation $invocation
114     *
115     * @return mixed
116     *
117     * @throws Exception
118     */
119    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
120    {
121        $exception      = null;
122        $hasReturnValue = false;
123        $returnValue    = null;
124
125        foreach ($this->matchers as $match) {
126            try {
127                if ($match->matches($invocation)) {
128                    $value = $match->invoked($invocation);
129
130                    if (!$hasReturnValue) {
131                        $returnValue    = $value;
132                        $hasReturnValue = true;
133                    }
134                }
135            } catch (Exception $e) {
136                $exception = $e;
137            }
138        }
139
140        if ($exception !== null) {
141            throw $exception;
142        }
143
144        if ($hasReturnValue) {
145            return $returnValue;
146        } elseif (strtolower($invocation->methodName) == '__tostring') {
147            return '';
148        }
149
150        return $invocation->generateReturnValue();
151    }
152
153    /**
154     * @param PHPUnit_Framework_MockObject_Invocation $invocation
155     *
156     * @return bool
157     */
158    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
159    {
160        foreach ($this->matchers as $matcher) {
161            if (!$matcher->matches($invocation)) {
162                return false;
163            }
164        }
165
166        return true;
167    }
168
169    /**
170     * @return bool
171     */
172    public function verify()
173    {
174        foreach ($this->matchers as $matcher) {
175            $matcher->verify();
176        }
177    }
178}
179