Completed
Push — master ( 047687...ae2c51 )
by Dave
02:41
created

Mockery   F

Complexity

Total Complexity 99

Size/Duplication

Total Lines 824
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 22

Test Coverage

Coverage 92.34%

Importance

Changes 0
Metric Value
wmc 99
lcom 3
cbo 22
dl 0
loc 824
ccs 229
cts 248
cp 0.9234
rs 1.263
c 0
b 0
f 0

46 Methods

Rating   Name   Duplication   Size   Complexity  
A mock() 0 6 1
A spy() 0 5 1
A instanceMock() 0 6 1
A namedMock() 0 12 1
A fetchMock() 0 4 1
A setGenerator() 0 4 1
A self() 0 8 2
A close() 0 15 3
A getContainer() 0 8 2
A getGenerator() 0 8 2
A getDefaultGenerator() 0 4 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
A noMoreElementsInChain() 0 4 1
A declareClass() 0 4 1
A declareInterface() 0 4 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\Loader\EvalLoader;
37
use Mockery\Loader\Loader;
38
39
class Mockery
40
{
41
    const BLOCKS = 'Mockery_Forward_Blocks';
42
43
    /**
44
     * Global container to hold all mocks for the current unit test running.
45
     *
46
     * @var \Mockery\Container
47
     */
48
    protected static $_container = null;
49
50
    /**
51
     * Global configuration handler containing configuration options.
52
     *
53
     * @var \Mockery\Configuration
54
     */
55
    protected static $_config = null;
56
57
    /**
58
     * @var \Mockery\Generator\Generator
59
     */
60
    protected static $_generator;
61
62
    /**
63
     * @var \Mockery\Loader\Loader
64
     */
65
    protected static $_loader;
66
67
    /**
68
     * @var array
69
     */
70
    private static $_filesToCleanUp = [];
71
72
    /**
73
     * Static shortcut to \Mockery\Container::mock().
74
     *
75
     * @return \Mockery\MockInterface
76
     */
77 45
    public static function mock()
78
    {
79 45
        $args = func_get_args();
80
81 45
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
82
    }
83
84
    /**
85
     * Static and semantic shortcut for getting a mock from the container
86
     * and applying the spy's expected behavior into it.
87
     *
88
     * @return \Mockery\MockInterface
89
     */
90 8
    public static function spy()
91
    {
92 8
        $args = func_get_args();
93 8
        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
94
    }
95
96
    /**
97
     * Static and Semantic shortcut to \Mockery\Container::mock().
98
     *
99
     * @return \Mockery\MockInterface
100
     */
101
    public static function instanceMock()
102
    {
103
        $args = func_get_args();
104
105
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
106
    }
107
108
    /**
109
     * Static shortcut to \Mockery\Container::mock(), first argument names the mock.
110
     *
111
     * @return \Mockery\MockInterface
112
     */
113 4
    public static function namedMock()
114
    {
115 4
        $args = func_get_args();
116 4
        $name = array_shift($args);
117
118 4
        $builder = new MockConfigurationBuilder();
119 4
        $builder->setName($name);
120
121 4
        array_unshift($args, $builder);
122
123 4
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
124
    }
125
126
    /**
127
     * Static shortcut to \Mockery\Container::self().
128
     *
129
     * @throws LogicException
130
     *
131
     * @return \Mockery\MockInterface
132
     */
133 2
    public static function self()
134
    {
135 2
        if (is_null(self::$_container)) {
136 1
            throw new \LogicException('You have not declared any mocks yet');
137
        }
138
139 1
        return self::$_container->self();
140
    }
141
142
    /**
143
     * Static shortcut to closing up and verifying all mocks in the global
144
     * container, and resetting the container static variable to null.
145
     *
146
     * @return void
147
     */
148 430
    public static function close()
149
    {
150 430
        foreach (self::$_filesToCleanUp as $fileName) {
151 18
            @unlink($fileName);
152
        }
153 430
        self::$_filesToCleanUp = [];
154
155 430
        if (is_null(self::$_container)) {
156
            return;
157
        }
158
159 430
        self::$_container->mockery_teardown();
160 430
        self::$_container->mockery_close();
161 430
        self::$_container = null;
162 430
    }
163
164
    /**
165
     * Static fetching of a mock associated with a name or explicit class poser.
166
     *
167
     * @param $name
168
     *
169
     * @return \Mockery\Mock
170
     */
171 11
    public static function fetchMock($name)
172
    {
173 11
        return self::$_container->fetchMock($name);
174
    }
175
176
    /**
177
     * Lazy loader and getter for
178
     * the container property.
179
     *
180
     * @return Mockery\Container
181
     */
182 459
    public static function getContainer()
183
    {
184 459
        if (is_null(self::$_container)) {
185 425
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
186
        }
187
188 459
        return self::$_container;
189
    }
190
191
    /**
192
     * Setter for the $_generator static propery.
193
     *
194
     * @param \Mockery\Generator\Generator $generator
195
     */
196
    public static function setGenerator(Generator $generator)
197
    {
198
        self::$_generator = $generator;
199
    }
200
201
    /**
202
     * Lazy loader method and getter for
203
     * the generator property.
204
     *
205
     * @return Generator
206
     */
207 425
    public static function getGenerator()
208
    {
209 425
        if (is_null(self::$_generator)) {
210 1
            self::$_generator = self::getDefaultGenerator();
211
        }
212
213 425
        return self::$_generator;
214
    }
215
216
    /**
217
     * Creates and returns a default generator
218
     * used inside this class.
219
     *
220
     * @return CachingGenerator
221
     */
222 425
    public static function getDefaultGenerator()
223
    {
224 425
        return new CachingGenerator(StringManipulationGenerator::withDefaultPasses());
225
    }
226
227
    /**
228
     * Setter for the $_loader static property.
229
     *
230
     * @param Loader $loader
231
     */
232
    public static function setLoader(Loader $loader)
233
    {
234
        self::$_loader = $loader;
235
    }
236
237
    /**
238
     * Lazy loader method and getter for
239
     * the $_loader property.
240
     *
241
     * @return Loader
242
     */
243 425
    public static function getLoader()
244
    {
245 425
        if (is_null(self::$_loader)) {
246 1
            self::$_loader = self::getDefaultLoader();
247
        }
248
249 425
        return self::$_loader;
250
    }
251
252
    /**
253
     * Gets an EvalLoader to be used as default.
254
     *
255
     * @return EvalLoader
256
     */
257 302
    public static function getDefaultLoader()
258
    {
259 302
        return new EvalLoader();
260
    }
261
262
    /**
263
     * Set the container.
264
     *
265
     * @param \Mockery\Container $container
266
     *
267
     * @return \Mockery\Container
268
     */
269 18
    public static function setContainer(Mockery\Container $container)
270
    {
271 18
        return self::$_container = $container;
272
    }
273
274
    /**
275
     * Reset the container to null.
276
     *
277
     * @return void
278
     */
279 14
    public static function resetContainer()
280
    {
281 14
        self::$_container = null;
282 14
    }
283
284
    /**
285
     * Return instance of ANY matcher.
286
     *
287
     * @return \Mockery\Matcher\Any
288
     */
289 5
    public static function any()
290
    {
291 5
        return new \Mockery\Matcher\Any();
292
    }
293
294
    /**
295
     * Return instance of TYPE matcher.
296
     *
297
     * @param $expected
298
     *
299
     * @return \Mockery\Matcher\Type
300
     */
301 47
    public static function type($expected)
302
    {
303 47
        return new \Mockery\Matcher\Type($expected);
304
    }
305
306
    /**
307
     * Return instance of DUCKTYPE matcher.
308
     *
309
     * @return \Mockery\Matcher\Ducktype
310
     */
311 3
    public static function ducktype()
312
    {
313 3
        return new \Mockery\Matcher\Ducktype(func_get_args());
314
    }
315
316
    /**
317
     * Return instance of SUBSET matcher.
318
     *
319
     * @param array $part
320
     *
321
     * @return \Mockery\Matcher\Subset
322
     */
323 3
    public static function subset(array $part)
324
    {
325 3
        return new \Mockery\Matcher\Subset($part);
326
    }
327
328
    /**
329
     * Return instance of CONTAINS matcher.
330
     *
331
     * @return \Mockery\Matcher\Contains
332
     */
333 3
    public static function contains()
334
    {
335 3
        return new \Mockery\Matcher\Contains(func_get_args());
336
    }
337
338
    /**
339
     * Return instance of HASKEY matcher.
340
     *
341
     * @param $key
342
     *
343
     * @return \Mockery\Matcher\HasKey
344
     */
345 3
    public static function hasKey($key)
346
    {
347 3
        return new \Mockery\Matcher\HasKey($key);
348
    }
349
350
    /**
351
     * Return instance of HASVALUE matcher.
352
     *
353
     * @param $val
354
     *
355
     * @return \Mockery\Matcher\HasValue
356
     */
357 3
    public static function hasValue($val)
358
    {
359 3
        return new \Mockery\Matcher\HasValue($val);
360
    }
361
362
    /**
363
     * Return instance of CLOSURE matcher.
364
     *
365
     * @param $closure
366
     *
367
     * @return \Mockery\Matcher\Closure
368
     */
369 6
    public static function on($closure)
370
    {
371 6
        return new \Mockery\Matcher\Closure($closure);
372
    }
373
374
    /**
375
     * Return instance of MUSTBE matcher.
376
     *
377
     * @param $expected
378
     *
379
     * @return \Mockery\Matcher\MustBe
380
     */
381 6
    public static function mustBe($expected)
382
    {
383 6
        return new \Mockery\Matcher\MustBe($expected);
384
    }
385
386
    /**
387
     * Return instance of NOT matcher.
388
     *
389
     * @param $expected
390
     *
391
     * @return \Mockery\Matcher\Not
392
     */
393 3
    public static function not($expected)
394
    {
395 3
        return new \Mockery\Matcher\Not($expected);
396
    }
397
398
    /**
399
     * Return instance of ANYOF matcher.
400
     *
401
     * @return \Mockery\Matcher\AnyOf
402
     */
403 3
    public static function anyOf()
404
    {
405 3
        return new \Mockery\Matcher\AnyOf(func_get_args());
406
    }
407
408
    /**
409
     * Return instance of NOTANYOF matcher.
410
     *
411
     * @return \Mockery\Matcher\NotAnyOf
412
     */
413 3
    public static function notAnyOf()
414
    {
415 3
        return new \Mockery\Matcher\NotAnyOf(func_get_args());
416
    }
417
418
    /**
419
     * Lazy loader and Getter for the global
420
     * configuration container.
421
     *
422
     * @return \Mockery\Configuration
423
     */
424 452
    public static function getConfiguration()
425
    {
426 452
        if (is_null(self::$_config)) {
427 1
            self::$_config = new \Mockery\Configuration();
428
        }
429
430 452
        return self::$_config;
431
    }
432
433
    /**
434
     * Utility method to format method name and arguments into a string.
435
     *
436
     * @param string $method
437
     * @param array $arguments
438
     *
439
     * @return string
440
     */
441 90
    public static function formatArgs($method, array $arguments = null)
442
    {
443 90
        if (is_null($arguments)) {
444
            return $method . '()';
445
        }
446
447 90
        $formattedArguments = array();
448 90
        foreach ($arguments as $argument) {
449 56
            $formattedArguments[] = self::formatArgument($argument);
450
        }
451
452 90
        return $method . '(' . implode(', ', $formattedArguments) . ')';
453
    }
454
455
    /**
456
     * Gets the string representation
457
     * of any passed argument.
458
     *
459
     * @param $argument
460
     * @param $depth
461
     *
462
     * @return string
463
     */
464 56
    private static function formatArgument($argument, $depth = 0)
465
    {
466 56
        if (is_object($argument)) {
467 8
            return 'object(' . get_class($argument) . ')';
468
        }
469
470 52
        if (is_int($argument) || is_float($argument)) {
471 35
            return $argument;
472
        }
473
474 26
        if (is_array($argument)) {
475 13
            if ($depth === 1) {
476 2
                $argument = '[...]';
477
            } else {
478 13
                $sample = array();
479 13
                foreach ($argument as $key => $value) {
480 12
                    $key = is_int($key) ? $key : "'$key'";
481 12
                    $value = self::formatArgument($value, $depth + 1);
482 12
                    $sample[] = "$key => $value";
483
                }
484
485 13
                $argument = "[".implode(", ", $sample)."]";
486
            }
487
488 13
            return ((strlen($argument) > 1000) ? substr($argument, 0, 1000).'...]' : $argument);
489
        }
490
491 17
        if (is_bool($argument)) {
492 1
            return $argument ? 'true' : 'false';
493
        }
494
495 16
        if (is_resource($argument)) {
496 2
            return 'resource(...)';
497
        }
498
499 14
        if (is_null($argument)) {
500 1
            return 'NULL';
501
        }
502
503 13
        return "'".(string) $argument."'";
504
    }
505
506
    /**
507
     * Utility function to format objects to printable arrays.
508
     *
509
     * @param array $objects
510
     *
511
     * @return string
512
     */
513 52
    public static function formatObjects(array $objects = null)
514
    {
515 52
        static $formatting;
516
517 52
        if ($formatting) {
518 1
            return '[Recursion]';
519
        }
520
521 52
        if (is_null($objects)) {
522
            return '';
523
        }
524
525 52
        $objects = array_filter($objects, 'is_object');
526 52
        if (empty($objects)) {
527 45
            return '';
528
        }
529
530 7
        $formatting = true;
531 7
        $parts = array();
532
533 7
        foreach ($objects as $object) {
534 7
            $parts[get_class($object)] = self::objectToArray($object);
535
        }
536
537 7
        $formatting = false;
538
539 7
        return 'Objects: ( ' . var_export($parts, true) . ')';
540
    }
541
542
    /**
543
     * Utility function to turn public properties and public get* and is* method values into an array.
544
     *
545
     * @param     $object
546
     * @param int $nesting
547
     *
548
     * @return array
549
     */
550 7
    private static function objectToArray($object, $nesting = 3)
551
    {
552 7
        if ($nesting == 0) {
553
            return array('...');
554
        }
555
556
        return array(
557 7
            'class' => get_class($object),
558 7
            'properties' => self::extractInstancePublicProperties($object, $nesting),
559 7
            'getters' => self::extractGetters($object, $nesting)
560
        );
561
    }
562
563
    /**
564
     * Returns all public instance properties.
565
     *
566
     * @param $object
567
     * @param $nesting
568
     *
569
     * @return array
570
     */
571 7
    private static function extractInstancePublicProperties($object, $nesting)
572
    {
573 7
        $reflection = new \ReflectionClass(get_class($object));
574 7
        $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
575 7
        $cleanedProperties = array();
576
577 7
        foreach ($properties as $publicProperty) {
578 1
            if (!$publicProperty->isStatic()) {
579
                $name = $publicProperty->getName();
580 1
                $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);
581
            }
582
        }
583
584 7
        return $cleanedProperties;
585
    }
586
587
    /**
588
     * Returns all object getters.
589
     *
590
     * @param $object
591
     * @param $nesting
592
     *
593
     * @return array
594
     */
595 7
    private static function extractGetters($object, $nesting)
596
    {
597 7
        $reflection = new \ReflectionClass(get_class($object));
598 7
        $publicMethods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
599 7
        $getters = array();
600
601 7
        foreach ($publicMethods as $publicMethod) {
602 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...
603 5
            $irrelevantName = (substr($name, 0, 3) !== 'get' && substr($name, 0, 2) !== 'is');
604 5
            $isStatic = $publicMethod->isStatic();
605 5
            $numberOfParameters = $publicMethod->getNumberOfParameters();
606
607 5
            if ($irrelevantName || $numberOfParameters != 0 || $isStatic) {
608 5
                continue;
609
            }
610
611
            try {
612 2
                $getters[$name] = self::cleanupNesting($object->$name(), $nesting);
613 1
            } catch (\Exception $e) {
614 2
                $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
615
            }
616
        }
617
618 7
        return $getters;
619
    }
620
621
    /**
622
     * Utility method used for recursively generating
623
     * an object or array representation.
624
     *
625
     * @param $argument
626
     * @param $nesting
627
     *
628
     * @return mixed
629
     */
630 1
    private static function cleanupNesting($argument, $nesting)
631
    {
632 1
        if (is_object($argument)) {
633
            $object = self::objectToArray($argument, $nesting - 1);
634
            $object['class'] = get_class($argument);
635
636
            return $object;
637
        }
638
639 1
        if (is_array($argument)) {
640 1
            return self::cleanupArray($argument, $nesting - 1);
641
        }
642
643 1
        return $argument;
644
    }
645
646
    /**
647
     * Utility method for recursively
648
     * gerating a representation
649
     * of the given array.
650
     *
651
     * @param array $argument
652
     * @param int $nesting
653
     *
654
     * @return mixed
655
     */
656 1
    private static function cleanupArray($argument, $nesting = 3)
657
    {
658 1
        if ($nesting == 0) {
659 1
            return '...';
660
        }
661
662 1
        foreach ($argument as $key => $value) {
663 1
            if (is_array($value)) {
664 1
                $argument[$key] = self::cleanupArray($value, $nesting - 1);
665
            } elseif (is_object($value)) {
666 1
                $argument[$key] = self::objectToArray($value, $nesting - 1);
667
            }
668
        }
669
670 1
        return $argument;
671
    }
672
673
    /**
674
     * Utility function to parse shouldReceive() arguments and generate
675
     * expectations from such as needed.
676
     *
677
     * @param Mockery\MockInterface $mock
678
     * @param array $args
679
     * @param callable $add
680
     * @return \Mockery\CompositeExpectation
681
     */
682 329
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
683
    {
684 329
        $composite = new \Mockery\CompositeExpectation();
685
686 329
        foreach ($args as $arg) {
687 329
            if (is_array($arg)) {
688 16
                foreach ($arg as $k => $v) {
689 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...
690 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...
691
                }
692
            } elseif (is_string($arg)) {
693 317
                $expectation = self::buildDemeterChain($mock, $arg, $add);
694 323
                $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...
695
            }
696
        }
697
698 323
        return $composite;
699
    }
700
701
    /**
702
     * Sets up expectations on the members of the CompositeExpectation and
703
     * builds up any demeter chain that was passed to shouldReceive.
704
     *
705
     * @param \Mockery\MockInterface $mock
706
     * @param string $arg
707
     * @param callable $add
708
     * @throws Mockery\Exception
709
     * @return \Mockery\ExpectationInterface
710
     */
711 329
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
712
    {
713
        /** @var Mockery\Container $container */
714 329
        $container = $mock->mockery_getContainer();
715 329
        $methodNames = explode('->', $arg);
716 329
        reset($methodNames);
717
718 329
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
719 329
            && !$mock->mockery_isAnonymous()
720 329
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
721
        ) {
722 4
            throw new \Mockery\Exception(
723
                'Mockery\'s configuration currently forbids mocking the method '
724 4
                . current($methodNames) . ' as it does not exist on the class or object '
725 4
                . 'being mocked'
726
            );
727
        }
728
729
        /** @var ExpectationInterface|null $expectations */
730 325
        $expectations = null;
731
732
        /** @var Callable $nextExp */
733
        $nextExp = function ($method) use ($add) {
734 325
            return $add($method);
735 325
        };
736
737 325
        while (true) {
738 325
            $method = array_shift($methodNames);
739 325
            $expectations = $mock->mockery_getExpectationsFor($method);
740
741 325
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
742 325
                $expectations = $nextExp($method);
743 323
                if (self::noMoreElementsInChain($methodNames)) {
744 323
                    break;
745
                }
746
747 11
                $mock = self::getNewDemeterMock($container, $method, $expectations);
748
            } else {
749 5
                $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
750 5
                if ($demeterMockKey) {
751 5
                    $mock = self::getExistingDemeterMock($container, $demeterMockKey);
752
                }
753
            }
754
755 11
            $nextExp = function ($n) use ($mock) {
756 11
                return $mock->shouldReceive($n);
757 11
            };
758
        }
759
760 323
        return $expectations;
761
    }
762
763
    /**
764
     * Gets a new demeter configured
765
     * mock from the container.
766
     *
767
     * @param \Mockery\Container $container
768
     * @param string $method
769
     * @param Mockery\ExpectationInterface $exp
770
     *
771
     * @return \Mockery\Mock
772
     */
773 11
    private static function getNewDemeterMock(
774
        Mockery\Container $container,
775
        $method,
776
        Mockery\ExpectationInterface $exp
777
    ) {
778 11
        $mock = $container->mock('demeter_' . $method);
779 11
        $exp->andReturn($mock);
780
781 11
        return $mock;
782
    }
783
784
    /**
785
     * Gets an specific demeter mock from
786
     * the ones kept by the container.
787
     *
788
     * @param \Mockery\Container $container
789
     * @param string $demeterMockKey
790
     *
791
     * @return mixed
792
     */
793 5
    private static function getExistingDemeterMock(
794
        Mockery\Container $container,
795
        $demeterMockKey
796
    ) {
797 5
        $mocks = $container->getMocks();
798 5
        $mock = $mocks[$demeterMockKey];
799
800 5
        return $mock;
801
    }
802
803
    /**
804
     * Checks if the passed array representing a demeter
805
     * chain with the method names is empty.
806
     *
807
     * @param array $methodNames
808
     *
809
     * @return bool
810
     */
811 323
    private static function noMoreElementsInChain(array $methodNames)
812
    {
813 323
        return empty($methodNames);
814
    }
815
816 17
    public static function declareClass($fqn)
817
    {
818 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...
819
    }
820
821 2
    public static function declareInterface($fqn)
822
    {
823 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...
824
    }
825
826 18
    private static function declareType($fqn, $type)
827
    {
828 18
        $targetCode = "<?php ";
829 18
        $shortName = $fqn;
830
831 18
        if (strpos($fqn, "\\")) {
832 4
            $parts = explode("\\", $fqn);
833
834 4
            $shortName = trim(array_pop($parts));
835 4
            $namespace = implode("\\", $parts);
836
837 4
            $targetCode.= "namespace $namespace;\n";
838
        }
839
840 18
        $targetCode.= "$type $shortName {} ";
841
842
        /*
843
         * We could eval here, but it doesn't play well with the way
844
         * PHPUnit tries to backup global state and the require definition
845
         * loader
846
         */
847 18
        $tmpfname = tempnam(sys_get_temp_dir(), "Mockery");
848 18
        file_put_contents($tmpfname, $targetCode);
849 18
        require $tmpfname;
850 18
        \Mockery::registerFileForCleanUp($tmpfname);
851 18
    }
852
853
    /**
854
     * Register a file to be deleted on tearDown.
855
     *
856
     * @param string $fileName
857
     */
858 18
    public static function registerFileForCleanUp($fileName)
859
    {
860 18
        self::$_filesToCleanUp[] = $fileName;
861 18
    }
862
}
863