| Code Coverage
| Classes and Traits | Functions and Methods | Lines
Total | 0.00% covered (danger) | 0.00% | 0 / 1 | 40.00% covered (danger) | 40.00% | 10 / 25 | CRAP | 57.40% covered (warning) | 57.40% | 97 / 169
MockConfiguration | 0.00% covered (danger) | 0.00% | 0 / 1 | 40.00% covered (danger) | 40.00% | 10 / 25 | 437.16 | 57.40% covered (warning) | 57.40% | 97 / 169
__construct(array $targets = array(), array $blackListedMethods = array(), array $whiteListedMethods = array(), $name = null, $instanceMock = false, array $parameterOverrides = array()) | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 7 / 7
getHash() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 9 / 9
getMethodsToMock() | 0.00% covered (danger) | 0.00% | 0 / 1 | 9.66 | 53.33% covered (warning) | 53.33% | 8 / 15
anonymous function ($method) use (&$names) | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.05 | 20.00% covered (danger) | 20.00% | 1 / 5
requiresCallTypeHintRemoval() | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.12 | 50.00% covered (warning) | 50.00% | 3 / 6
requiresCallStaticTypeHintRemoval() | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.12 | 50.00% covered (warning) | 50.00% | 3 / 6
rename($className) | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.04 | 86.67% covered (warning) | 86.67% | 13 / 15
addTarget($target) | 0.00% covered (danger) | 0.00% | 0 / 1 | 15.75 | 35.29% covered (danger) | 35.29% | 6 / 17
addTargets($interfaces) | 100.00% covered (success) | 100.00% | 1 / 1 | 2 | 100.00% covered (success) | 100.00% | 4 / 4
getTargetClassName() | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 | 0.00% covered (danger) | 0.00% | 0 / 1
getTargetClass() | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.64 | 64.29% covered (warning) | 64.29% | 9 / 14
getTargetInterfaces() | 0.00% covered (danger) | 0.00% | 0 / 1 | 110.80 | 16.67% covered (danger) | 16.67% | 5 / 30
getTargetObject() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 1 / 1
getName() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 1 / 1
generateName() | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.02 | 60.00% covered (warning) | 60.00% | 6 / 10
getShortName() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 2 / 2
getNamespaceName() | 0.00% covered (danger) | 0.00% | 0 / 1 | 2.03 | 80.00% covered (warning) | 80.00% | 4 / 5
getBlackListedMethods() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 1 / 1
getWhiteListedMethods() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 1 / 1
isInstanceMock() | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 1 / 1
getParameterOverrides() | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 | 0.00% covered (danger) | 0.00% | 0 / 1
setTargetClassName($targetClassName) | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 2 / 2
getAllMethods() | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.03 | 90.00% covered (success) | 90.00% | 9 / 10
addTargetInterfaceName($targetInterface) | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 | 0.00% covered (danger) | 0.00% | 0 / 2
setTargetObject($object) | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 | 0.00% covered (danger) | 0.00% | 0 / 2

1 | <?php
2 |
3 | namespace Mockery\Generator;
4 |
5 | /**
6 | * This class describes the configuration of mocks and hides away some of the
7 | * reflection implementation
8 | */
9 | class MockConfiguration
10 | {
11 | protected static $mockCounter = 0;
12 |
13 | /**
14 | * A class that we'd like to mock
15 | */
16 | protected $targetClass;
17 | protected $targetClassName;
18 |
19 | /**
20 | * A number of interfaces we'd like to mock, keyed by name to attempt to
21 | * keep unique
22 | */
23 | protected $targetInterfaces = array();
24 | protected $targetInterfaceNames = array();
25 |
26 | /**
27 | * An object we'd like our mock to proxy to
28 | */
29 | protected $targetObject;
30 |
31 | /**
32 | * The class name we'd like to use for a generated mock
33 | */
34 | protected $name;
35 |
36 | /**
37 | * Methods that should specifically not be mocked
38 | *
39 | * This is currently populated with stuff we don't know how to deal with,
40 | * should really be somewhere else
41 | */
42 | protected $blackListedMethods = array();
43 |
44 | /**
45 | * If not empty, only these methods will be mocked
46 | */
47 | protected $whiteListedMethods = array();
48 |
49 | /**
50 | * An instance mock is where we override the original class before it's
51 | * autoloaded
52 | */
53 | protected $instanceMock = false;
54 |
55 | /**
56 | * Param overrides
57 | */
58 | protected $parameterOverrides = array();
59 |
60 | /**
61 | * Instance cache of all methods
62 | */
63 | protected $allMethods;
64 |
65 | public function __construct(array $targets = array(), array $blackListedMethods = array(), array $whiteListedMethods = array(), $name = null, $instanceMock = false, array $parameterOverrides = array())
66 | {
67 | $this->addTargets($targets);
68 | $this->blackListedMethods = $blackListedMethods;
69 | $this->whiteListedMethods = $whiteListedMethods;
70 | $this->name = $name;
71 | $this->instanceMock = $instanceMock;
72 | $this->parameterOverrides = $parameterOverrides;
73 | }
74 |
75 | /**
76 | * Attempt to create a hash of the configuration, in order to allow caching
77 | *
78 | * @TODO workout if this will work
79 | *
80 | * @return string
81 | */
82 | public function getHash()
83 | {
84 | $vars = array(
85 | 'targetClassName' => $this->targetClassName,
86 | 'targetInterfaceNames' => $this->targetInterfaceNames,
87 | 'name' => $this->name,
88 | 'blackListedMethods' => $this->blackListedMethods,
89 | 'whiteListedMethod' => $this->whiteListedMethods,
90 | 'instanceMock' => $this->instanceMock,
91 | 'parameterOverrides' => $this->parameterOverrides,
92 | );
93 |
94 | return md5(serialize($vars));
95 | }
96 |
97 | /**
98 | * Gets a list of methods from the classes, interfaces and objects and
99 | * filters them appropriately. Lot's of filtering going on, perhaps we could
100 | * have filter classes to iterate through
101 | */
102 | public function getMethodsToMock()
103 | {
104 | $methods = $this->getAllMethods();
105 |
106 | foreach ($methods as $key => $method) {
107 | if ($method->isFinal()) {
108 | unset($methods[$key]);
109 | }
110 | }
111 |
112 | /**
113 | * Whitelist trumps blacklist
114 | */
115 | if (count($this->getWhiteListedMethods())) {
116 | $whitelist = array_map('strtolower', $this->getWhiteListedMethods());
117 | $methods = array_filter($methods, function ($method) use ($whitelist) {
118 | return $method->isAbstract() || in_array(strtolower($method->getName()), $whitelist);
119 | });
120 |
121 | return $methods;
122 | }
123 |
124 | /**
125 | * Remove blacklisted methods
126 | */
127 | if (count($this->getBlackListedMethods())) {
128 | $blacklist = array_map('strtolower', $this->getBlackListedMethods());
129 | $methods = array_filter($methods, function ($method) use ($blacklist) {
130 | return !in_array(strtolower($method->getName()), $blacklist);
131 | });
132 | }
133 |
134 | return array_values($methods);
135 | }
136 |
137 | /**
138 | * We declare the __call method to handle undefined stuff, if the class
139 | * we're mocking has also defined it, we need to comply with their interface
140 | */
141 | public function requiresCallTypeHintRemoval()
142 | {
143 | foreach ($this->getAllMethods() as $method) {
144 | if ("__call" === $method->getName()) {
145 | $params = $method->getParameters();
146 | return !$params[1]->isArray();
147 | }
148 | }
149 |
150 | return false;
151 | }
152 |
153 | /**
154 | * We declare the __callStatic method to handle undefined stuff, if the class
155 | * we're mocking has also defined it, we need to comply with their interface
156 | */
157 | public function requiresCallStaticTypeHintRemoval()
158 | {
159 | foreach ($this->getAllMethods() as $method) {
160 | if ("__callStatic" === $method->getName()) {
161 | $params = $method->getParameters();
162 | return !$params[1]->isArray();
163 | }
164 | }
165 |
166 | return false;
167 | }
168 |
169 | public function rename($className)
170 | {
171 | $targets = array();
172 |
173 | if ($this->targetClassName) {
174 | $targets[] = $this->targetClassName;
175 | }
176 |
177 | if ($this->targetInterfaceNames) {
178 | $targets = array_merge($targets, $this->targetInterfaceNames);
179 | }
180 |
181 | if ($this->targetObject) {
182 | $targets[] = $this->targetObject;
183 | }
184 |
185 | return new self(
186 | $targets,
187 | $this->blackListedMethods,
188 | $this->whiteListedMethods,
189 | $className,
190 | $this->instanceMock,
191 | $this->parameterOverrides
192 | );
193 | }
194 |
195 | protected function addTarget($target)
196 | {
197 | if (is_object($target)) {
198 | $this->setTargetObject($target);
199 | $this->setTargetClassName(get_class($target));
200 | return $this;
201 | }
202 |
203 | if ($target[0] !== "\\") {
204 | $target = "\\" . $target;
205 | }
206 |
207 | if (class_exists($target)) {
208 | $this->setTargetClassName($target);
209 | return $this;
210 | }
211 |
212 | if (interface_exists($target)) {
213 | $this->addTargetInterfaceName($target);
214 | return $this;
215 | }
216 |
217 | /**
218 | * Default is to set as class, or interface if class already set
219 | *
220 | * Don't like this condition, can't remember what the default
221 | * targetClass is for
222 | */
223 | if ($this->getTargetClassName()) {
224 | $this->addTargetInterfaceName($target);
225 | return $this;
226 | }
227 |
228 | $this->setTargetClassName($target);
229 | }
230 |
231 | protected function addTargets($interfaces)
232 | {
233 | foreach ($interfaces as $interface) {
234 | $this->addTarget($interface);
235 | }
236 | }
237 |
238 | public function getTargetClassName()
239 | {
240 | return $this->targetClassName;
241 | }
242 |
243 | public function getTargetClass()
244 | {
245 | if ($this->targetClass) {
246 | return $this->targetClass;
247 | }
248 |
249 | if (!$this->targetClassName) {
250 | return null;
251 | }
252 |
253 | if (class_exists($this->targetClassName)) {
254 | $dtc = DefinedTargetClass::factory($this->targetClassName);
255 |
256 | if (!$this->getTargetObject() && $dtc->isFinal()) {
257 | throw new \Mockery\Exception(
258 | 'The class ' . $this->targetClassName . ' is marked final and its methods'
259 | . ' cannot be replaced. Classes marked final can be passed in'
260 | . ' to \Mockery::mock() as instantiated objects to create a'
261 | . ' partial mock, but only if the mock is not subject to type'
262 | . ' hinting checks.'
263 | );
264 | }
265 |
266 | $this->targetClass = $dtc;
267 |
268 | } else {
269 | $this->targetClass = new UndefinedTargetClass($this->targetClassName);
270 | }
271 |
272 | return $this->targetClass;
273 | }
274 |
275 | public function getTargetInterfaces()
276 | {
277 | if (!empty($this->targetInterfaces)) {
278 | return $this->targetInterfaces;
279 | }
280 |
281 | foreach ($this->targetInterfaceNames as $targetInterface) {
282 | if (!interface_exists($targetInterface)) {
283 | $this->targetInterfaces[] = new UndefinedTargetClass($targetInterface);
284 | return;
285 | }
286 |
287 | $dtc = DefinedTargetClass::factory($targetInterface);
288 | $extendedInterfaces = array_keys($dtc->getInterfaces());
289 | $extendedInterfaces[] = $targetInterface;
290 |
291 | $traversableFound = false;
292 | $iteratorShiftedToFront = false;
293 | foreach ($extendedInterfaces as $interface) {
294 |
295 | if (!$traversableFound && preg_match("/^\\?Iterator(|Aggregate)$/i", $interface)) {
296 | break;
297 | }
298 |
299 | if (preg_match("/^\\\\?IteratorAggregate$/i", $interface)) {
300 | $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
301 | $iteratorShiftedToFront = true;
302 | } elseif (preg_match("/^\\\\?Iterator$/i", $interface)) {
303 | $this->targetInterfaces[] = DefinedTargetClass::factory("\\Iterator");
304 | $iteratorShiftedToFront = true;
305 | } elseif (preg_match("/^\\\\?Traversable$/i", $interface)) {
306 | $traversableFound = true;
307 | }
308 | }
309 |
310 | if ($traversableFound && !$iteratorShiftedToFront) {
311 | $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
312 | }
313 |
314 | /**
315 | * We never straight up implement Traversable
316 | */
317 | if (!preg_match("/^\\\\?Traversable$/i", $targetInterface)) {
318 | $this->targetInterfaces[] = $dtc;
319 | }
320 |
321 | }
322 | $this->targetInterfaces = array_unique($this->targetInterfaces); // just in case
323 | return $this->targetInterfaces;
324 | }
325 |
326 | public function getTargetObject()
327 | {
328 | return $this->targetObject;
329 | }
330 |
331 | public function getName()
332 | {
333 | return $this->name;
334 | }
335 |
336 | /**
337 | * Generate a suitable name based on the config
338 | */
339 | public function generateName()
340 | {
341 | $name = 'Mockery_' . static::$mockCounter++;
342 |
343 | if ($this->getTargetObject()) {
344 | $name .= "_" . str_replace("\\", "_", get_class($this->getTargetObject()));
345 | }
346 |
347 | if ($this->getTargetClass()) {
348 | $name .= "_" . str_replace("\\", "_", $this->getTargetClass()->getName());
349 | }
350 |
351 | if ($this->getTargetInterfaces()) {
352 | $name .= array_reduce($this->getTargetInterfaces(), function ($tmpname, $i) {
353 | $tmpname .= '_' . str_replace("\\", "_", $i->getName());
354 | return $tmpname;
355 | }, '');
356 | }
357 |
358 | return $name;
359 | }
360 |
361 | public function getShortName()
362 | {
363 | $parts = explode("\\", $this->getName());
364 | return array_pop($parts);
365 | }
366 |
367 | public function getNamespaceName()
368 | {
369 | $parts = explode("\\", $this->getName());
370 | array_pop($parts);
371 |
372 | if (count($parts)) {
373 | return implode("\\", $parts);
374 | }
375 |
376 | return "";
377 | }
378 |
379 | public function getBlackListedMethods()
380 | {
381 | return $this->blackListedMethods;
382 | }
383 |
384 | public function getWhiteListedMethods()
385 | {
386 | return $this->whiteListedMethods;
387 | }
388 |
389 | public function isInstanceMock()
390 | {
391 | return $this->instanceMock;
392 | }
393 |
394 | public function getParameterOverrides()
395 | {
396 | return $this->parameterOverrides;
397 | }
398 |
399 | protected function setTargetClassName($targetClassName)
400 | {
401 | $this->targetClassName = $targetClassName;
402 | }
403 |
404 | protected function getAllMethods()
405 | {
406 | if ($this->allMethods) {
407 | return $this->allMethods;
408 | }
409 |
410 | $classes = $this->getTargetInterfaces();
411 |
412 | if ($this->getTargetClass()) {
413 | $classes[] = $this->getTargetClass();
414 | }
415 |
416 | $methods = array();
417 | foreach ($classes as $class) {
418 | $methods = array_merge($methods, $class->getMethods());
419 | }
420 |
421 | $names = array();
422 | $methods = array_filter($methods, function ($method) use (&$names) {
423 | if (in_array($method->getName(), $names)) {
424 | return false;
425 | }
426 |
427 | $names[] = $method->getName();
428 | return true;
429 | });
430 |
431 | return $this->allMethods = $methods;
432 | }
433 |
434 | /**
435 | * If we attempt to implement Traversable, we must ensure we are also
436 | * implementing either Iterator or IteratorAggregate, and that whichever one
437 | * it is comes before Traversable in the list of implements.
438 | */
439 | protected function addTargetInterfaceName($targetInterface)
440 | {
441 | $this->targetInterfaceNames[] = $targetInterface;
442 | }
443 |
444 |
445 | protected function setTargetObject($object)
446 | {
447 | $this->targetObject = $object;
448 | }
449 |
450 | }