Completed
Pull Request — master (#668)
by Dave
03:18
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.63%

Importance

Changes 0
Metric Value
wmc 99
lcom 3
cbo 33
dl 0
loc 838
ccs 264
cts 285
cp 0.9263
rs 1.0434
c 0
b 0
f 0

46 Methods

Rating   Name   Duplication   Size   Complexity  
A getGenerator() 0 8 2
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 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;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Generator.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
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 32
    public static function mock()
79
    {
80 32
        $args = func_get_args();
81
82 32
        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 405
    public static function close()
150
    {
151 405
        foreach (self::$_filesToCleanUp as $fileName) {
152 18
            @unlink($fileName);
153 405
        }
154 405
        self::$_filesToCleanUp = [];
155
156 405
        if (is_null(self::$_container)) {
157
            return;
158
        }
159
160 405
        self::$_container->mockery_teardown();
161 403
        self::$_container->mockery_close();
162 403
        self::$_container = null;
163 403
    }
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 424
    public static function getContainer()
184
    {
185 424
        if (is_null(self::$_container)) {
186 398
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
187 398
        }
188
189 424
        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 398
    public static function getGenerator()
209
    {
210 398
        if (is_null(self::$_generator)) {
211 1
            self::$_generator = self::getDefaultGenerator();
212 1
        }
213
214 398
        return self::$_generator;
215
    }
216
217
    /**
218
     * Creates and returns a default generator
219
     * used inside this class.
220
     *
221
     * @return CachingGenerator
222
     */
223 398
    public static function getDefaultGenerator()
224
    {
225 398
        $generator = new StringManipulationGenerator(array(
226 398
            new CallTypeHintPass(),
227 398
            new MagicMethodTypeHintsPass(),
228 398
            new ClassPass(),
229 398
            new ClassNamePass(),
230 398
            new InstanceMockPass(),
231 398
            new InterfacePass(),
232 398
            new AvoidMethodClashPass(),
233 398
            new MethodDefinitionPass(),
234 398
            new RemoveUnserializeForInternalSerializableClassesPass(),
235 398
            new RemoveBuiltinMethodsThatAreFinalPass(),
236 398
            new RemoveDestructorPass(),
237 398
        ));
238
239 398
        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 398
    public static function getLoader()
259
    {
260 398
        if (is_null(self::$_loader)) {
261 1
            self::$_loader = self::getDefaultLoader();
262 1
        }
263
264 398
        return self::$_loader;
265
    }
266
267
    /**
268
     * Gets an EvalLoader to be used as default.
269
     *
270
     * @return EvalLoader
271
     */
272 272
    public static function getDefaultLoader()
273
    {
274 272
        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 6
    public static function mustBe($expected)
397
    {
398 6
        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 417
    public static function getConfiguration()
440
    {
441 417
        if (is_null(self::$_config)) {
442 1
            self::$_config = new \Mockery\Configuration();
443 1
        }
444
445 417
        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 92
    public static function formatArgs($method, array $arguments = null)
457
    {
458 92
        if (is_null($arguments)) {
459
            return $method . '()';
460
        }
461
462 92
        $formattedArguments = array();
463 92
        foreach ($arguments as $argument) {
464 58
            $formattedArguments[] = self::formatArgument($argument);
465 92
        }
466
467 92
        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 58
    private static function formatArgument($argument, $depth = 0)
480
    {
481 58
        if (is_object($argument)) {
482 8
            return 'object(' . get_class($argument) . ')';
483
        }
484
485 54
        if (is_int($argument) || is_float($argument)) {
486 37
            return $argument;
487
        }
488
489 26
        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 17
        if (is_bool($argument)) {
507 1
            return $argument ? 'true' : 'false';
508
        }
509
510 16
        if (is_resource($argument)) {
511 2
            return 'resource(...)';
512
        }
513
514 14
        if (is_null($argument)) {
515 1
            return 'NULL';
516
        }
517
518 13
        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 52
    public static function formatObjects(array $objects = null)
529
    {
530 52
        static $formatting;
531
532 52
        if ($formatting) {
533 1
            return '[Recursion]';
534
        }
535
536 52
        if (is_null($objects)) {
537
            return '';
538
        }
539
540 52
        $objects = array_filter($objects, 'is_object');
541 52
        if (empty($objects)) {
542 45
            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();
0 ignored issues
show
Bug introduced by
Consider using $publicMethod->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
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 304
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
698
    {
699 304
        $composite = new \Mockery\CompositeExpectation();
700
701 304
        foreach ($args as $arg) {
702 304
            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 304
            } elseif (is_string($arg)) {
708 292
                $expectation = self::buildDemeterChain($mock, $arg, $add);
709 286
                $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 286
            }
711 298
        }
712
713 298
        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 304
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
727
    {
728
        /** @var Mockery\Container $container */
729 304
        $container = $mock->mockery_getContainer();
730 304
        $methodNames = explode('->', $arg);
731 304
        reset($methodNames);
732
733 304
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
734 304
            && !$mock->mockery_isAnonymous()
735 304
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
736 304
        ) {
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 300
        $expectations = null;
746
747
        /** @var Callable $nextExp */
748
        $nextExp = function ($method) use ($add) {
749 300
            return $add($method);
750 300
        };
751
752 300
        while (true) {
753 300
            $method = array_shift($methodNames);
754 300
            $expectations = $mock->mockery_getExpectationsFor($method);
755
756 300
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
757 300
                $expectations = $nextExp($method);
758 298
                if (self::noMoreElementsInChain($methodNames)) {
759 298
                    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 298
        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 298
    private static function noMoreElementsInChain(array $methodNames)
827
    {
828 298
        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