1 : <?php
2 : /**
3 : * Mockery
4 : *
5 : * LICENSE
6 : *
7 : * This source file is subject to the new BSD license that is bundled
8 : * with this package in the file LICENSE.txt.
9 : * It is also available through the world-wide-web at this URL:
10 : * http://github.com/padraic/mockery/blob/master/LICENSE
11 : * If you did not receive a copy of the license and are unable to
12 : * obtain it through the world-wide-web, please send an email
13 : * to padraic@php.net so we can send you a copy immediately.
14 : *
15 : * @category Mockery
16 : * @package Mockery
17 : * @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
18 : * @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
19 : */
20 :
21 : namespace Mockery;
22 :
23 : class Container
24 : {
25 : const BLOCKS = \Mockery::BLOCKS;
26 :
27 : /**
28 : * Store of mock objects
29 : *
30 : * @var array
31 : */
32 : protected $_mocks = array();
33 :
34 : /**
35 : * Order number of allocation
36 : *
37 : * @var int
38 : */
39 : protected $_allocatedOrder = 0;
40 :
41 : /**
42 : * Current ordered number
43 : *
44 : * @var int
45 : */
46 : protected $_currentOrder = 0;
47 :
48 : /**
49 : * Ordered groups
50 : *
51 : * @var array
52 : */
53 : protected $_groups = array();
54 :
55 : /**
56 : * Generates a new mock object for this container
57 : *
58 : * I apologies in advance for this. A God Method just fits the API which
59 : * doesn't require differentiating between classes, interfaces, abstracts,
60 : * names or partials - just so long as it's something that can be mocked.
61 : * I'll refactor it one day so it's easier to follow.
62 : *
63 : * @return \Mockery\Mock
64 : */
65 : public function mock()
66 : {
67 26 : $class = null;
68 26 : $name = null;
69 26 : $partial = null;
70 26 : $expectationClosure = null;
71 26 : $quickdefs = array();
72 26 : $blocks = array();
73 26 : $makeInstanceMock = false;
74 26 : $args = func_get_args();
75 26 : $partialMethods = array();
76 26 : if (count($args) > 1) {
77 0 : $finalArg = end($args);
78 0 : reset($args);
79 0 : if (is_callable($finalArg)) {
80 0 : $expectationClosure = array_pop($args);
81 0 : }
82 0 : }
83 26 : while (count($args) > 0) {
84 26 : $arg = current($args);
85 : // check for multiple interfaces
86 26 : if (is_string($arg) && strpos($arg, ',') && !strpos($arg, ']')) {
87 0 : $interfaces = explode(',', str_replace(' ', '', $arg));
88 0 : foreach ($interfaces as $i) {
89 0 : if (!interface_exists($i, true) && !class_exists($i, true)) {
90 0 : throw new \Mockery\Exception(
91 : 'Class name follows the format for defining multiple'
92 : . ' interfaces, however one or more of the interfaces'
93 0 : . ' do not exist or are not included, or the base class'
94 0 : . ' (optional) does not exist'
95 0 : );
96 : }
97 0 : }
98 0 : $class = $interfaces;
99 0 : array_shift($args);
100 26 : } elseif (is_string($arg) && substr($arg, 0, 6) == 'alias:') {
101 0 : $class = 'stdClass';
102 0 : $name = array_shift($args);
103 0 : $name = str_replace('alias:', '', $name);
104 26 : } elseif (is_string($arg) && substr($arg, 0, 9) == 'overload:') {
105 0 : $class = 'stdClass';
106 0 : $name = array_shift($args);
107 0 : $name = str_replace('overload:', '', $name);
108 0 : $makeInstanceMock = true;
109 26 : } elseif (is_string($arg) && substr($arg, strlen($arg)-1, 1) == ']') {
110 0 : $parts = explode('[', $arg);
111 0 : if (!class_exists($parts[0], true) && !interface_exists($parts[0], true)) {
112 0 : throw new \Mockery\Exception('Can only create a partial mock from'
113 0 : . ' an existing class or interface');
114 : }
115 0 : $class = $parts[0];
116 0 : $parts[1] = str_replace(' ','', $parts[1]);
117 0 : $partialMethods = explode(',', strtolower(rtrim($parts[1], ']')));
118 0 : array_shift($args);
119 26 : } elseif (is_string($arg) && (class_exists($arg, true) || interface_exists($arg, true))) {
120 0 : $class = array_shift($args);
121 26 : } elseif (is_string($arg)) {
122 0 : $name = array_shift($args);
123 26 : } elseif (is_object($arg)) {
124 26 : $partial = array_shift($args);
125 26 : } elseif (is_array($arg)) {
126 0 : if(array_key_exists(self::BLOCKS, $arg)) $blocks = $arg[self::BLOCKS]; unset($arg[self::BLOCKS]);
127 0 : $quickdefs = array_shift($args);
128 0 : } else {
129 0 : throw new \Mockery\Exception(
130 : 'Unable to parse arguments sent to '
131 0 : . get_class($this) . '::mock()'
132 0 : );
133 : }
134 26 : }
135 26 : if (!is_null($name) && !is_null($class)) {
136 0 : if (!$makeInstanceMock) {
137 0 : $mockName = \Mockery\Generator::createClassMock($class);
138 0 : } else {
139 0 : $mockName = \Mockery\Generator::createClassMock($class, null, null, array(), true);
140 : }
141 0 : $result = class_alias($mockName, $name);
142 0 : $mock = $this->_getInstance($name);
143 0 : $mock->mockery_init($class, $this);
144 26 : } elseif (!is_null($name)) {
145 0 : $mock = new \Mockery\Mock();
146 0 : $mock->mockery_init($name, $this);
147 26 : } elseif(!is_null($class)) {
148 0 : $mockName = \Mockery\Generator::createClassMock($class, null, null, array(), false, $partialMethods);
149 0 : $mock = $this->_getInstance($mockName);
150 0 : $mock->mockery_init($class, $this);
151 26 : } elseif(!is_null($partial)) {
152 26 : $mockName = \Mockery\Generator::createClassMock(get_class($partial), null, true, $blocks);
153 26 : $mock = $this->_getInstance($mockName);
154 26 : $mock->mockery_init(get_class($partial), $this, $partial);
155 26 : } else {
156 0 : $mock = new \Mockery\Mock();
157 0 : $mock->mockery_init('unknown', $this);
158 : }
159 26 : if (!empty($quickdefs)) {
160 0 : $mock->shouldReceive($quickdefs);
161 0 : }
162 26 : if (!empty($expectationClosure)) {
163 0 : $expectationClosure($mock);
164 0 : }
165 26 : $this->rememberMock($mock);
166 26 : return $mock;
167 : }
168 :
169 : public function instanceMock()
170 : {
171 :
172 0 : }
173 :
174 : /**
175 : * Tear down tasks for this container
176 : *
177 : * @return void
178 : */
179 : public function mockery_teardown()
180 : {
181 : try {
182 24 : $this->mockery_verify();
183 24 : } catch (\Exception $e) {
184 0 : $this->mockery_close();
185 0 : throw $e;
186 : }
187 24 : }
188 :
189 : /**
190 : * Verify the container mocks
191 : *
192 : * @return void
193 : */
194 : public function mockery_verify()
195 : {
196 24 : foreach($this->_mocks as $mock) {
197 24 : $mock->mockery_verify();
198 24 : }
199 24 : }
200 :
201 : /**
202 : * Reset the container to its original state
203 : *
204 : * @return void
205 : */
206 : public function mockery_close()
207 : {
208 24 : foreach($this->_mocks as $mock) {
209 24 : $mock->mockery_teardown();
210 24 : }
211 24 : $this->_mocks = array();
212 24 : }
213 :
214 : /**
215 : * Fetch the next available allocation order number
216 : *
217 : * @return int
218 : */
219 : public function mockery_allocateOrder()
220 : {
221 0 : $this->_allocatedOrder += 1;
222 0 : return $this->_allocatedOrder;
223 : }
224 :
225 : /**
226 : * Set ordering for a group
227 : *
228 : * @param mixed $group
229 : * @param int $order
230 : */
231 : public function mockery_setGroup($group, $order)
232 : {
233 0 : $this->_groups[$group] = $order;
234 0 : }
235 :
236 : /**
237 : * Fetch array of ordered groups
238 : *
239 : * @return array
240 : */
241 : public function mockery_getGroups()
242 : {
243 0 : return $this->_groups;
244 : }
245 :
246 : /**
247 : * Set current ordered number
248 : *
249 : * @param int $order
250 : * @return int The current order number that was set
251 : */
252 : public function mockery_setCurrentOrder($order)
253 : {
254 0 : $this->_currentOrder = $order;
255 0 : return $this->_currentOrder;
256 : }
257 :
258 : /**
259 : * Get current ordered number
260 : *
261 : * @return int
262 : */
263 : public function mockery_getCurrentOrder()
264 : {
265 0 : return $this->_currentOrder;
266 : }
267 :
268 : /**
269 : * Validate the current mock's ordering
270 : *
271 : * @param string $method
272 : * @param int $order
273 : * @throws \Mockery\Exception
274 : * @return void
275 : */
276 : public function mockery_validateOrder($method, $order)
277 : {
278 0 : if ($order < $this->_currentOrder) {
279 0 : throw new \Mockery\Exception(
280 0 : 'Method ' . $method . ' called out of order: expected order '
281 0 : . $order . ', was ' . $this->_currentOrder
282 0 : );
283 : }
284 0 : $this->mockery_setCurrentOrder($order);
285 0 : }
286 :
287 : /**
288 : * Store a mock and set its container reference
289 : *
290 : * @param \Mockery\Mock
291 : * @return \Mockery\Mock
292 : */
293 : public function rememberMock(\Mockery\MockInterface $mock)
294 : {
295 26 : if (!isset($this->_mocks[get_class($mock)])) {
296 26 : $this->_mocks[get_class($mock)] = $mock;
297 26 : } else {
298 : /**
299 : * This condition triggers for an instance mock where origin mock
300 : * is already remembered
301 : */
302 0 : $this->_mocks[] = $mock;
303 : }
304 26 : return $mock;
305 : }
306 :
307 : /**
308 : * Retrieve the last remembered mock object, which is the same as saying
309 : * retrieve the current mock being programmed where you have yet to call
310 : * mock() to change it - thus why the method name is "self" since it will be
311 : * be used during the programming of the same mock.
312 : *
313 : * @return \Mockery\Mock
314 : */
315 : public function self()
316 : {
317 0 : $mocks = array_values($this->_mocks);
318 0 : $index = count($mocks) - 1;
319 0 : return $mocks[$index];
320 : }
321 :
322 : /**
323 : * Return a specific remembered mock according to the array index it
324 : * was stored to in this container instance
325 : *
326 : * @return \Mockery\Mock
327 : */
328 : public function fetchMock($reference)
329 : {
330 0 : if (isset($this->_mocks[$reference])) return $this->_mocks[$reference];
331 0 : }
332 :
333 : protected function _getInstance($mockName)
334 : {
335 26 : if (!method_exists($mockName, '__construct')) {
336 0 : $return = new $mockName;
337 0 : return $return;
338 : }
339 26 : $return = unserialize(sprintf('O:%d:"%s":0:{}', strlen($mockName), $mockName));
340 26 : return $return;
341 : }
342 :
343 : }
|