Completed
Pull Request — master (#668)
by Dave
04:20 queued 02:13
created

Mockery   F

Complexity

Total Complexity 99

Size/Duplication

Total Lines 838
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 33

Test Coverage

Coverage 92.66%

Importance

Changes 0
Metric Value
wmc 99
lcom 3
cbo 33
dl 0
loc 838
ccs 265
cts 286
cp 0.9266
rs 1.0434
c 0
b 0
f 0

46 Methods

Rating   Name   Duplication   Size   Complexity  
A noMoreElementsInChain() 0 4 1
A declareClass() 0 4 1
A declareInterface() 0 4 1
A mock() 0 6 1
A spy() 0 5 1
A instanceMock() 0 6 1
A namedMock() 0 12 1
A self() 0 8 2
A close() 0 15 3
A fetchMock() 0 4 1
A getContainer() 0 8 2
A setGenerator() 0 4 1
A getGenerator() 0 8 2
A getDefaultGenerator() 0 18 1
A setLoader() 0 4 1
A getLoader() 0 8 2
A getDefaultLoader() 0 4 1
A setContainer() 0 4 1
A resetContainer() 0 4 1
A any() 0 4 1
A type() 0 4 1
A ducktype() 0 4 1
A subset() 0 4 1
A contains() 0 4 1
A hasKey() 0 4 1
A hasValue() 0 4 1
A on() 0 4 1
A mustBe() 0 4 1
A not() 0 4 1
A anyOf() 0 4 1
A notAnyOf() 0 4 1
A getConfiguration() 0 8 2
A formatArgs() 0 13 3
C formatArgument() 0 41 13
B formatObjects() 0 28 5
A objectToArray() 0 12 2
A extractInstancePublicProperties() 0 15 3
C extractGetters() 0 25 7
A cleanupNesting() 0 15 3
B cleanupArray() 0 16 5
B parseShouldReturnArgs() 0 18 5
C buildDemeterChain() 0 51 9
A getNewDemeterMock() 0 10 1
A getExistingDemeterMock() 0 9 1
B declareType() 0 26 2
A registerFileForCleanUp() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Mockery often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Mockery, and based on these observations, apply Extract Interface, too.

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 [email protected] 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
use Mockery\ExpectationInterface;
22
use Mockery\Generator\CachingGenerator;
23
use Mockery\Generator\Generator;
24
use Mockery\Generator\MockConfigurationBuilder;
25
use Mockery\Generator\StringManipulation\Pass\RemoveDestructorPass;
26
use Mockery\Generator\StringManipulationGenerator;
27
use Mockery\Generator\StringManipulation\Pass\CallTypeHintPass;
28
use Mockery\Generator\StringManipulation\Pass\MagicMethodTypeHintsPass;
29
use Mockery\Generator\StringManipulation\Pass\ClassNamePass;
30
use Mockery\Generator\StringManipulation\Pass\ClassPass;
31
use Mockery\Generator\StringManipulation\Pass\InstanceMockPass;
32
use Mockery\Generator\StringManipulation\Pass\InterfacePass;
33
use Mockery\Generator\StringManipulation\Pass\MethodDefinitionPass;
34
use Mockery\Generator\StringManipulation\Pass\RemoveBuiltinMethodsThatAreFinalPass;
35
use Mockery\Generator\StringManipulation\Pass\RemoveUnserializeForInternalSerializableClassesPass;
36
use Mockery\Generator\StringManipulation\Pass\AvoidMethodClashPass;
37
use Mockery\Loader\EvalLoader;
38
use Mockery\Loader\Loader;
39
40
class Mockery
41
{
42
    const BLOCKS = 'Mockery_Forward_Blocks';
43
44
    /**
45
     * Global container to hold all mocks for the current unit test running.
46
     *
47
     * @var \Mockery\Container
48
     */
49
    protected static $_container = null;
50
51
    /**
52
     * Global configuration handler containing configuration options.
53
     *
54
     * @var \Mockery\Configuration
55
     */
56
    protected static $_config = null;
57
58
    /**
59
     * @var \Mockery\Generator\Generator
60
     */
61
    protected static $_generator;
62
63
    /**
64
     * @var \Mockery\Loader\Loader
65
     */
66
    protected static $_loader;
67
68
    /**
69
     * @var array
70
     */
71
    private static $_filesToCleanUp = [];
72
73
    /**
74
     * Static shortcut to \Mockery\Container::mock().
75
     *
76
     * @return \Mockery\MockInterface
77
     */
78 30
    public static function mock()
79
    {
80 30
        $args = func_get_args();
81
82 30
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
83
    }
84
85
    /**
86
     * Static and semantic shortcut for getting a mock from the container
87
     * and applying the spy's expected behavior into it.
88
     *
89
     * @return \Mockery\MockInterface
90
     */
91 7
    public static function spy()
92
    {
93 7
        $args = func_get_args();
94 7
        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
95
    }
96
97
    /**
98
     * Static and Semantic shortcut to \Mockery\Container::mock().
99
     *
100
     * @return \Mockery\MockInterface
101
     */
102
    public static function instanceMock()
103
    {
104
        $args = func_get_args();
105
106
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
107
    }
108
109
    /**
110
     * Static shortcut to \Mockery\Container::mock(), first argument names the mock.
111
     *
112
     * @return \Mockery\MockInterface
113
     */
114 4
    public static function namedMock()
115
    {
116 4
        $args = func_get_args();
117 4
        $name = array_shift($args);
118
119 4
        $builder = new MockConfigurationBuilder();
120 4
        $builder->setName($name);
121
122 4
        array_unshift($args, $builder);
123
124 4
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
125
    }
126
127
    /**
128
     * Static shortcut to \Mockery\Container::self().
129
     *
130
     * @throws LogicException
131
     *
132
     * @return \Mockery\MockInterface
133
     */
134 2
    public static function self()
135
    {
136 2
        if (is_null(self::$_container)) {
137 1
            throw new \LogicException('You have not declared any mocks yet');
138
        }
139
140 1
        return self::$_container->self();
141
    }
142
143
    /**
144
     * Static shortcut to closing up and verifying all mocks in the global
145
     * container, and resetting the container static variable to null.
146
     *
147
     * @return void
148
     */
149 415
    public static function close()
150
    {
151 415
        foreach (self::$_filesToCleanUp as $fileName) {
152 18
            @unlink($fileName);
153 415
        }
154 415
        self::$_filesToCleanUp = [];
155
156 415
        if (is_null(self::$_container)) {
157
            return;
158
        }
159
160 415
        self::$_container->mockery_teardown();
161 414
        self::$_container->mockery_close();
162 414
        self::$_container = null;
163 414
    }
164
165
    /**
166
     * Static fetching of a mock associated with a name or explicit class poser.
167
     *
168
     * @param $name
169
     *
170
     * @return \Mockery\Mock
171
     */
172 11
    public static function fetchMock($name)
173
    {
174 11
        return self::$_container->fetchMock($name);
175
    }
176
177
    /**
178
     * Lazy loader and getter for
179
     * the container property.
180
     *
181
     * @return Mockery\Container
182
     */
183 433
    public static function getContainer()
184
    {
185 433
        if (is_null(self::$_container)) {
186 408
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
187 408
        }
188
189 433
        return self::$_container;
190
    }
191
192
    /**
193
     * Setter for the $_generator static propery.
194
     *
195
     * @param \Mockery\Generator\Generator $generator
196
     */
197
    public static function setGenerator(Generator $generator)
198
    {
199
        self::$_generator = $generator;
200
    }
201
202
    /**
203
     * Lazy loader method and getter for
204
     * the generator property.
205
     *
206
     * @return Generator
207
     */
208 408
    public static function getGenerator()
209
    {
210 408
        if (is_null(self::$_generator)) {
211 1
            self::$_generator = self::getDefaultGenerator();
212 1
        }
213
214 408
        return self::$_generator;
215 1
    }
216
217
    /**
218
     * Creates and returns a default generator
219
     * used inside this class.
220
     *
221
     * @return CachingGenerator
222
     */
223 409
    public static function getDefaultGenerator()
224
    {
225 409
        $generator = new StringManipulationGenerator(array(
226 409
            new CallTypeHintPass(),
227 409
            new MagicMethodTypeHintsPass(),
228 409
            new ClassPass(),
229 409
            new ClassNamePass(),
230 409
            new InstanceMockPass(),
231 409
            new InterfacePass(),
232 409
            new AvoidMethodClashPass(),
233 409
            new MethodDefinitionPass(),
234 409
            new RemoveUnserializeForInternalSerializableClassesPass(),
235 409
            new RemoveBuiltinMethodsThatAreFinalPass(),
236 409
            new RemoveDestructorPass(),
237 409
        ));
238
239 409
        return new CachingGenerator($generator);
240
    }
241
242
    /**
243
     * Setter for the $_loader static property.
244
     *
245
     * @param Loader $loader
246
     */
247
    public static function setLoader(Loader $loader)
248
    {
249
        self::$_loader = $loader;
250
    }
251
252
    /**
253
     * Lazy loader method and getter for
254
     * the $_loader property.
255
     *
256
     * @return Loader
257
     */
258 408
    public static function getLoader()
259
    {
260 408
        if (is_null(self::$_loader)) {
261 1
            self::$_loader = self::getDefaultLoader();
262 1
        }
263
264 408
        return self::$_loader;
265
    }
266
267
    /**
268
     * Gets an EvalLoader to be used as default.
269
     *
270
     * @return EvalLoader
271
     */
272 283
    public static function getDefaultLoader()
273
    {
274 283
        return new EvalLoader();
275
    }
276
277
    /**
278
     * Set the container.
279
     *
280
     * @param \Mockery\Container $container
281
     *
282
     * @return \Mockery\Container
283
     */
284 18
    public static function setContainer(Mockery\Container $container)
285
    {
286 18
        return self::$_container = $container;
287
    }
288
289
    /**
290
     * Reset the container to null.
291
     *
292
     * @return void
293
     */
294 15
    public static function resetContainer()
295
    {
296 15
        self::$_container = null;
297 15
    }
298
299
    /**
300
     * Return instance of ANY matcher.
301
     *
302
     * @return \Mockery\Matcher\Any
303
     */
304 5
    public static function any()
305
    {
306 5
        return new \Mockery\Matcher\Any();
307
    }
308
309
    /**
310
     * Return instance of TYPE matcher.
311
     *
312
     * @param $expected
313
     *
314
     * @return \Mockery\Matcher\Type
315
     */
316 48
    public static function type($expected)
317
    {
318 48
        return new \Mockery\Matcher\Type($expected);
319
    }
320
321
    /**
322
     * Return instance of DUCKTYPE matcher.
323
     *
324
     * @return \Mockery\Matcher\Ducktype
325
     */
326 3
    public static function ducktype()
327
    {
328 3
        return new \Mockery\Matcher\Ducktype(func_get_args());
329
    }
330
331
    /**
332
     * Return instance of SUBSET matcher.
333
     *
334
     * @param array $part
335
     *
336
     * @return \Mockery\Matcher\Subset
337
     */
338 3
    public static function subset(array $part)
339
    {
340 3
        return new \Mockery\Matcher\Subset($part);
341
    }
342
343
    /**
344
     * Return instance of CONTAINS matcher.
345
     *
346
     * @return \Mockery\Matcher\Contains
347
     */
348 3
    public static function contains()
349
    {
350 3
        return new \Mockery\Matcher\Contains(func_get_args());
351
    }
352
353
    /**
354
     * Return instance of HASKEY matcher.
355
     *
356
     * @param $key
357
     *
358
     * @return \Mockery\Matcher\HasKey
359
     */
360 3
    public static function hasKey($key)
361
    {
362 3
        return new \Mockery\Matcher\HasKey($key);
363
    }
364
365
    /**
366
     * Return instance of HASVALUE matcher.
367
     *
368
     * @param $val
369
     *
370
     * @return \Mockery\Matcher\HasValue
371
     */
372 3
    public static function hasValue($val)
373
    {
374 3
        return new \Mockery\Matcher\HasValue($val);
375
    }
376
377
    /**
378
     * Return instance of CLOSURE matcher.
379
     *
380
     * @param $closure
381
     *
382
     * @return \Mockery\Matcher\Closure
383
     */
384 7
    public static function on($closure)
385
    {
386 7
        return new \Mockery\Matcher\Closure($closure);
387
    }
388
389
    /**
390
     * Return instance of MUSTBE matcher.
391
     *
392
     * @param $expected
393
     *
394
     * @return \Mockery\Matcher\MustBe
395
     */
396 9
    public static function mustBe($expected)
397
    {
398 9
        return new \Mockery\Matcher\MustBe($expected);
399
    }
400
401
    /**
402
     * Return instance of NOT matcher.
403
     *
404
     * @param $expected
405
     *
406
     * @return \Mockery\Matcher\Not
407
     */
408 3
    public static function not($expected)
409
    {
410 3
        return new \Mockery\Matcher\Not($expected);
411
    }
412
413
    /**
414
     * Return instance of ANYOF matcher.
415
     *
416
     * @return \Mockery\Matcher\AnyOf
417
     */
418 3
    public static function anyOf()
419
    {
420 3
        return new \Mockery\Matcher\AnyOf(func_get_args());
421
    }
422
423
    /**
424
     * Return instance of NOTANYOF matcher.
425
     *
426
     * @return \Mockery\Matcher\NotAnyOf
427
     */
428 3
    public static function notAnyOf()
429
    {
430 3
        return new \Mockery\Matcher\NotAnyOf(func_get_args());
431
    }
432
433
    /**
434
     * Lazy loader and Getter for the global
435
     * configuration container.
436
     *
437
     * @return \Mockery\Configuration
438
     */
439 426
    public static function getConfiguration()
440
    {
441 426
        if (is_null(self::$_config)) {
442 1
            self::$_config = new \Mockery\Configuration();
443 1
        }
444
445 426
        return self::$_config;
446
    }
447
448
    /**
449
     * Utility method to format method name and arguments into a string.
450
     *
451
     * @param string $method
452
     * @param array $arguments
453
     *
454
     * @return string
455
     */
456 95
    public static function formatArgs($method, array $arguments = null)
457
    {
458 95
        if (is_null($arguments)) {
459
            return $method . '()';
460
        }
461
462 95
        $formattedArguments = array();
463 95
        foreach ($arguments as $argument) {
464 61
            $formattedArguments[] = self::formatArgument($argument);
465 95
        }
466
467 95
        return $method . '(' . implode(', ', $formattedArguments) . ')';
468
    }
469
470
    /**
471
     * Gets the string representation
472
     * of any passed argument.
473
     *
474
     * @param $argument
475
     * @param $depth
476
     *
477
     * @return string
478
     */
479 61
    private static function formatArgument($argument, $depth = 0)
480
    {
481 61
        if (is_object($argument)) {
482 10
            return 'object(' . get_class($argument) . ')';
483
        }
484
485 55
        if (is_int($argument) || is_float($argument)) {
486 37
            return $argument;
487
        }
488
489 27
        if (is_array($argument)) {
490 13
            if ($depth === 1) {
491 2
                $argument = '[...]';
492 2
            } else {
493 13
                $sample = array();
494 13
                foreach ($argument as $key => $value) {
495 12
                    $key = is_int($key) ? $key : "'$key'";
496 12
                    $value = self::formatArgument($value, $depth + 1);
497 12
                    $sample[] = "$key => $value";
498 13
                }
499
500 13
                $argument = "[".implode(", ", $sample)."]";
501
            }
502
503 13
            return ((strlen($argument) > 1000) ? substr($argument, 0, 1000).'...]' : $argument);
504
        }
505
506 18
        if (is_bool($argument)) {
507 1
            return $argument ? 'true' : 'false';
508
        }
509
510 17
        if (is_resource($argument)) {
511 2
            return 'resource(...)';
512
        }
513
514 15
        if (is_null($argument)) {
515 1
            return 'NULL';
516
        }
517
518 14
        return "'".(string) $argument."'";
519
    }
520
521
    /**
522
     * Utility function to format objects to printable arrays.
523
     *
524
     * @param array $objects
525
     *
526
     * @return string
527
     */
528 54
    public static function formatObjects(array $objects = null)
529
    {
530 54
        static $formatting;
531
532 54
        if ($formatting) {
533 1
            return '[Recursion]';
534
        }
535
536 54
        if (is_null($objects)) {
537
            return '';
538
        }
539
540 54
        $objects = array_filter($objects, 'is_object');
541 54
        if (empty($objects)) {
542 47
            return '';
543
        }
544
545 7
        $formatting = true;
546 7
        $parts = array();
547
548 7
        foreach ($objects as $object) {
549 7
            $parts[get_class($object)] = self::objectToArray($object);
550 7
        }
551
552 7
        $formatting = false;
553
554 7
        return 'Objects: ( ' . var_export($parts, true) . ')';
555
    }
556
557
    /**
558
     * Utility function to turn public properties and public get* and is* method values into an array.
559
     *
560
     * @param     $object
561
     * @param int $nesting
562
     *
563
     * @return array
564
     */
565 7
    private static function objectToArray($object, $nesting = 3)
566
    {
567 7
        if ($nesting == 0) {
568
            return array('...');
569
        }
570
571
        return array(
572 7
            'class' => get_class($object),
573 7
            'properties' => self::extractInstancePublicProperties($object, $nesting),
574 7
            'getters' => self::extractGetters($object, $nesting)
575 7
        );
576
    }
577
578
    /**
579
     * Returns all public instance properties.
580
     *
581
     * @param $object
582
     * @param $nesting
583
     *
584
     * @return array
585
     */
586 7
    private static function extractInstancePublicProperties($object, $nesting)
587
    {
588 7
        $reflection = new \ReflectionClass(get_class($object));
589 7
        $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
590 7
        $cleanedProperties = array();
591
592 7
        foreach ($properties as $publicProperty) {
593 1
            if (!$publicProperty->isStatic()) {
594
                $name = $publicProperty->getName();
595
                $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);
596
            }
597 7
        }
598
599 7
        return $cleanedProperties;
600
    }
601
602
    /**
603
     * Returns all object getters.
604
     *
605
     * @param $object
606
     * @param $nesting
607
     *
608
     * @return array
609
     */
610 7
    private static function extractGetters($object, $nesting)
611
    {
612 7
        $reflection = new \ReflectionClass(get_class($object));
613 7
        $publicMethods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
614 7
        $getters = array();
615
616 7
        foreach ($publicMethods as $publicMethod) {
617 5
            $name = $publicMethod->getName();
618 5
            $irrelevantName = (substr($name, 0, 3) !== 'get' && substr($name, 0, 2) !== 'is');
619 5
            $isStatic = $publicMethod->isStatic();
620 5
            $numberOfParameters = $publicMethod->getNumberOfParameters();
621
622 5
            if ($irrelevantName || $numberOfParameters != 0 || $isStatic) {
623 5
                continue;
624
            }
625
626
            try {
627 2
                $getters[$name] = self::cleanupNesting($object->$name(), $nesting);
628 2
            } catch (\Exception $e) {
629 1
                $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
630
            }
631 7
        }
632
633 7
        return $getters;
634
    }
635
636
    /**
637
     * Utility method used for recursively generating
638
     * an object or array representation.
639
     *
640
     * @param $argument
641
     * @param $nesting
642
     *
643
     * @return mixed
644
     */
645 1
    private static function cleanupNesting($argument, $nesting)
646
    {
647 1
        if (is_object($argument)) {
648
            $object = self::objectToArray($argument, $nesting - 1);
649
            $object['class'] = get_class($argument);
650
651
            return $object;
652
        }
653
654 1
        if (is_array($argument)) {
655 1
            return self::cleanupArray($argument, $nesting - 1);
656
        }
657
658 1
        return $argument;
659
    }
660
661
    /**
662
     * Utility method for recursively
663
     * gerating a representation
664
     * of the given array.
665
     *
666
     * @param array $argument
667
     * @param int $nesting
668
     *
669
     * @return mixed
670
     */
671 1
    private static function cleanupArray($argument, $nesting = 3)
672
    {
673 1
        if ($nesting == 0) {
674 1
            return '...';
675
        }
676
677 1
        foreach ($argument as $key => $value) {
678 1
            if (is_array($value)) {
679 1
                $argument[$key] = self::cleanupArray($value, $nesting - 1);
680 1
            } elseif (is_object($value)) {
681
                $argument[$key] = self::objectToArray($value, $nesting - 1);
682
            }
683 1
        }
684
685 1
        return $argument;
686
    }
687
688
    /**
689
     * Utility function to parse shouldReceive() arguments and generate
690
     * expectations from such as needed.
691
     *
692
     * @param Mockery\MockInterface $mock
693
     * @param array $args
694
     * @param callable $add
695
     * @return \Mockery\CompositeExpectation
696
     */
697 313
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
698
    {
699 313
        $composite = new \Mockery\CompositeExpectation();
700
701 313
        foreach ($args as $arg) {
702 313
            if (is_array($arg)) {
703 16
                foreach ($arg as $k => $v) {
704 16
                    $expectation = self::buildDemeterChain($mock, $k, $add)->andReturn($v);
0 ignored issues
show
Unused Code introduced by
The call to ExpectationInterface::andReturn() has too many arguments starting with $v.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
705 16
                    $composite->add($expectation);
0 ignored issues
show
Documentation introduced by
$expectation is of type object<Mockery\ExpectationInterface>, but the function expects a object<Mockery\Expectati...y\CompositeExpectation>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
706 16
                }
707 313
            } elseif (is_string($arg)) {
708 301
                $expectation = self::buildDemeterChain($mock, $arg, $add);
709 295
                $composite->add($expectation);
0 ignored issues
show
Documentation introduced by
$expectation is of type object<Mockery\ExpectationInterface>, but the function expects a object<Mockery\Expectati...y\CompositeExpectation>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
710 295
            }
711 307
        }
712
713 307
        return $composite;
714
    }
715
716
    /**
717
     * Sets up expectations on the members of the CompositeExpectation and
718
     * builds up any demeter chain that was passed to shouldReceive.
719
     *
720
     * @param \Mockery\MockInterface $mock
721
     * @param string $arg
722
     * @param callable $add
723
     * @throws Mockery\Exception
724
     * @return \Mockery\ExpectationInterface
725
     */
726 313
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
727
    {
728
        /** @var Mockery\Container $container */
729 313
        $container = $mock->mockery_getContainer();
730 313
        $methodNames = explode('->', $arg);
731 313
        reset($methodNames);
732
733 313
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
734 313
            && !$mock->mockery_isAnonymous()
735 313
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
736 313
        ) {
737 4
            throw new \Mockery\Exception(
738
                'Mockery\'s configuration currently forbids mocking the method '
739 4
                . current($methodNames) . ' as it does not exist on the class or object '
740 4
                . 'being mocked'
741 4
            );
742
        }
743
744
        /** @var ExpectationInterface|null $expectations */
745 309
        $expectations = null;
746
747
        /** @var Callable $nextExp */
748
        $nextExp = function ($method) use ($add) {
749 309
            return $add($method);
750 309
        };
751
752 309
        while (true) {
753 309
            $method = array_shift($methodNames);
754 309
            $expectations = $mock->mockery_getExpectationsFor($method);
755
756 309
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
757 309
                $expectations = $nextExp($method);
758 307
                if (self::noMoreElementsInChain($methodNames)) {
759 307
                    break;
760
                }
761
762 11
                $mock = self::getNewDemeterMock($container, $method, $expectations);
763 11
            } else {
764 5
                $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
765 5
                if ($demeterMockKey) {
766 5
                    $mock = self::getExistingDemeterMock($container, $demeterMockKey);
767 5
                }
768
            }
769
770 11
            $nextExp = function ($n) use ($mock) {
771 11
                return $mock->shouldReceive($n);
772 11
            };
773 11
        }
774
775 307
        return $expectations;
776
    }
777
778
    /**
779
     * Gets a new demeter configured
780
     * mock from the container.
781
     *
782
     * @param \Mockery\Container $container
783
     * @param string $method
784
     * @param Mockery\ExpectationInterface $exp
785
     *
786
     * @return \Mockery\Mock
787
     */
788 11
    private static function getNewDemeterMock(
789
        Mockery\Container $container,
790
        $method,
791
        Mockery\ExpectationInterface $exp
792
    ) {
793 11
        $mock = $container->mock('demeter_' . $method);
794 11
        $exp->andReturn($mock);
795
796 11
        return $mock;
797
    }
798
799
    /**
800
     * Gets an specific demeter mock from
801
     * the ones kept by the container.
802
     *
803
     * @param \Mockery\Container $container
804
     * @param string $demeterMockKey
805
     *
806
     * @return mixed
807
     */
808 5
    private static function getExistingDemeterMock(
809
        Mockery\Container $container,
810
        $demeterMockKey
811
    ) {
812 5
        $mocks = $container->getMocks();
813 5
        $mock = $mocks[$demeterMockKey];
814
815 5
        return $mock;
816
    }
817
818
    /**
819
     * Checks if the passed array representing a demeter
820
     * chain with the method names is empty.
821
     *
822
     * @param array $methodNames
823
     *
824
     * @return bool
825
     */
826 307
    private static function noMoreElementsInChain(array $methodNames)
827
    {
828 307
        return empty($methodNames);
829
    }
830
831 17
    public static function declareClass($fqn)
832
    {
833 17
        return static::declareType($fqn, "class");
0 ignored issues
show
Bug introduced by
Since declareType() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of declareType() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
834
    }
835
836 2
    public static function declareInterface($fqn)
837
    {
838 2
        return static::declareType($fqn, "interface");
0 ignored issues
show
Bug introduced by
Since declareType() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of declareType() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
839
    }
840
841 18
    private static function declareType($fqn, $type)
842
    {
843 18
        $targetCode = "<?php ";
844 18
        $shortName = $fqn;
845
846 18
        if (strpos($fqn, "\\")) {
847 4
            $parts = explode("\\", $fqn);
848
849 4
            $shortName = trim(array_pop($parts));
850 4
            $namespace = implode("\\", $parts);
851
852 4
            $targetCode.= "namespace $namespace;\n";
853 4
        }
854
855 18
        $targetCode.= "$type $shortName {} ";
856
857
        /*
858
         * We could eval here, but it doesn't play well with the way
859
         * PHPUnit tries to backup global state and the require definition
860
         * loader
861
         */
862 18
        $tmpfname = tempnam(sys_get_temp_dir(), "Mockery");
863 18
        file_put_contents($tmpfname, $targetCode);
864 18
        require $tmpfname;
865 18
        \Mockery::registerFileForCleanUp($tmpfname);
866 18
    }
867
868
    /**
869
     * Register a file to be deleted on tearDown.
870
     *
871
     * @param string $fileName
872
     */
873 18
    public static function registerFileForCleanUp($fileName)
874
    {
875 18
        self::$_filesToCleanUp[] = $fileName;
876 18
    }
877
}
878