Completed
Pull Request — master (#712)
by Dave
02:48
created

Mockery::formatObjects()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0073

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 14
cts 15
cp 0.9333
rs 8.439
c 0
b 0
f 0
cc 5
eloc 15
nc 5
nop 1
crap 5.0073
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\StringManipulationGenerator;
26
use Mockery\Loader\EvalLoader;
27
use Mockery\Loader\Loader;
28
use Mockery\Matcher\MatcherAbstract;
29
use Mockery\ClosureWrapper;
30
31
class Mockery
32
{
33
    const BLOCKS = 'Mockery_Forward_Blocks';
34
35
    /**
36
     * Global container to hold all mocks for the current unit test running.
37
     *
38
     * @var \Mockery\Container
39
     */
40
    protected static $_container = null;
41
42
    /**
43
     * Global configuration handler containing configuration options.
44
     *
45
     * @var \Mockery\Configuration
46
     */
47
    protected static $_config = null;
48
49
    /**
50
     * @var \Mockery\Generator\Generator
51
     */
52
    protected static $_generator;
53
54
    /**
55
     * @var \Mockery\Loader\Loader
56
     */
57
    protected static $_loader;
58
59
    /**
60
     * @var array
61
     */
62
    private static $_filesToCleanUp = [];
63
64
    /**
65
     * Defines the global helper functions
66
     * 
67
     * @return void
68
     */
69 3
    public static function globalHelpers()
70
    {
71 3
        require_once __DIR__.'/helpers.php';
72 3
    }
73
74
    /**
75
     * Static shortcut to \Mockery\Container::mock().
76
     *
77
     * @param array $args
78
     *
79
     * @return \Mockery\MockInterface
80
     */
81 60
    public static function mock(...$args)
82
    {
83 60
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
84
    }
85
86
    /**
87
     * Static and semantic shortcut for getting a mock from the container
88
     * and applying the spy's expected behavior into it.
89
     *
90
     * @param array $args
91
     *
92
     * @return \Mockery\MockInterface
93
     */
94 26
    public static function spy(...$args)
95
    {
96 26
        if (count($args) && $args[0] instanceof \Closure) {
97 16
            $args[0] = new ClosureWrapper($args[0]);
98
        }
99
100 26
        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
101
    }
102
103
    /**
104
     * Static and Semantic shortcut to \Mockery\Container::mock().
105
     *
106
     * @param array $args
107
     *
108
     * @return \Mockery\MockInterface
109
     */
110
    public static function instanceMock(...$args)
111
    {
112
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
113
    }
114
115
    /**
116
     * Static shortcut to \Mockery\Container::mock(), first argument names the mock.
117
     *
118
     * @param array $args
119
     *
120
     * @return \Mockery\MockInterface
121
     */
122 5
    public static function namedMock(...$args)
123
    {
124 5
        $name = array_shift($args);
125
126 5
        $builder = new MockConfigurationBuilder();
127 5
        $builder->setName($name);
128
129 5
        array_unshift($args, $builder);
130
131 5
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
132
    }
133
134
    /**
135
     * Static shortcut to \Mockery\Container::self().
136
     *
137
     * @throws LogicException
138
     *
139
     * @return \Mockery\MockInterface
140
     */
141 2
    public static function self()
142
    {
143 2
        if (is_null(self::$_container)) {
144 1
            throw new \LogicException('You have not declared any mocks yet');
145
        }
146
147 1
        return self::$_container->self();
148
    }
149
150
    /**
151
     * Static shortcut to closing up and verifying all mocks in the global
152
     * container, and resetting the container static variable to null.
153
     *
154
     * @return void
155
     */
156 460
    public static function close()
157
    {
158 460
        foreach (self::$_filesToCleanUp as $fileName) {
159 18
            @unlink($fileName);
160
        }
161 460
        self::$_filesToCleanUp = [];
162
163 460
        if (is_null(self::$_container)) {
164
            return;
165
        }
166
167 460
        self::$_container->mockery_teardown();
168 458
        self::$_container->mockery_close();
169 458
        self::$_container = null;
170 458
    }
171
172
    /**
173
     * Static fetching of a mock associated with a name or explicit class poser.
174
     *
175
     * @param $name
176
     *
177
     * @return \Mockery\Mock
178
     */
179 11
    public static function fetchMock($name)
180
    {
181 11
        return self::$_container->fetchMock($name);
182
    }
183
184
    /**
185
     * Lazy loader and getter for
186
     * the container property.
187
     *
188
     * @return Mockery\Container
189
     */
190 498
    public static function getContainer()
191
    {
192 498
        if (is_null(self::$_container)) {
193 453
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
194
        }
195
196 498
        return self::$_container;
197
    }
198
199
    /**
200
     * Setter for the $_generator static propery.
201
     *
202
     * @param \Mockery\Generator\Generator $generator
203
     */
204
    public static function setGenerator(Generator $generator)
205
    {
206
        self::$_generator = $generator;
207
    }
208
209
    /**
210
     * Lazy loader method and getter for
211
     * the generator property.
212
     *
213
     * @return Generator
214
     */
215 453
    public static function getGenerator()
216
    {
217 453
        if (is_null(self::$_generator)) {
218 1
            self::$_generator = self::getDefaultGenerator();
219
        }
220
221 453
        return self::$_generator;
222
    }
223
224
    /**
225
     * Creates and returns a default generator
226
     * used inside this class.
227
     *
228
     * @return CachingGenerator
229
     */
230 430
    public static function getDefaultGenerator()
231
    {
232 430
        return new CachingGenerator(StringManipulationGenerator::withDefaultPasses());
233
    }
234
235
    /**
236
     * Setter for the $_loader static property.
237
     *
238
     * @param Loader $loader
239
     */
240
    public static function setLoader(Loader $loader)
241
    {
242
        self::$_loader = $loader;
243
    }
244
245
    /**
246
     * Lazy loader method and getter for
247
     * the $_loader property.
248
     *
249
     * @return Loader
250
     */
251 453
    public static function getLoader()
252
    {
253 453
        if (is_null(self::$_loader)) {
254 1
            self::$_loader = self::getDefaultLoader();
255
        }
256
257 453
        return self::$_loader;
258
    }
259
260
    /**
261
     * Gets an EvalLoader to be used as default.
262
     *
263
     * @return EvalLoader
264
     */
265 307
    public static function getDefaultLoader()
266
    {
267 307
        return new EvalLoader();
268
    }
269
270
    /**
271
     * Set the container.
272
     *
273
     * @param \Mockery\Container $container
274
     *
275
     * @return \Mockery\Container
276
     */
277 18
    public static function setContainer(Mockery\Container $container)
278
    {
279 18
        return self::$_container = $container;
280
    }
281
282
    /**
283
     * Reset the container to null.
284
     *
285
     * @return void
286
     */
287 14
    public static function resetContainer()
288
    {
289 14
        self::$_container = null;
290 14
    }
291
292
    /**
293
     * Return instance of ANY matcher.
294
     *
295
     * @return \Mockery\Matcher\Any
296
     */
297 5
    public static function any()
298
    {
299 5
        return new \Mockery\Matcher\Any();
300
    }
301
302
    /**
303
     * Return instance of TYPE matcher.
304
     *
305
     * @param $expected
306
     *
307
     * @return \Mockery\Matcher\Type
308
     */
309 47
    public static function type($expected)
310
    {
311 47
        return new \Mockery\Matcher\Type($expected);
312
    }
313
314
    /**
315
     * Return instance of DUCKTYPE matcher.
316
     *
317
     * @param array $args
318
     *
319
     * @return \Mockery\Matcher\Ducktype
320
     */
321 3
    public static function ducktype(...$args)
322
    {
323 3
        return new \Mockery\Matcher\Ducktype($args);
324
    }
325
326
    /**
327
     * Return instance of SUBSET matcher.
328
     *
329
     * @param array $part
330
     * @param bool $strict - (Optional) True for strict comparison, false for loose
331
     *
332
     * @return \Mockery\Matcher\Subset
333
     */
334 3
    public static function subset(array $part, $strict = true)
335
    {
336 3
        return new \Mockery\Matcher\Subset($part, $strict);
337
    }
338
339
    /**
340
     * Return instance of CONTAINS matcher.
341
     *
342
     * @param array $args
343
     *
344
     * @return \Mockery\Matcher\Contains
345
     */
346 3
    public static function contains(...$args)
347
    {
348 3
        return new \Mockery\Matcher\Contains($args);
349
    }
350
351
    /**
352
     * Return instance of HASKEY matcher.
353
     *
354
     * @param $key
355
     *
356
     * @return \Mockery\Matcher\HasKey
357
     */
358 4
    public static function hasKey($key)
359
    {
360 4
        return new \Mockery\Matcher\HasKey($key);
361
    }
362
363
    /**
364
     * Return instance of HASVALUE matcher.
365
     *
366
     * @param $val
367
     *
368
     * @return \Mockery\Matcher\HasValue
369
     */
370 3
    public static function hasValue($val)
371
    {
372 3
        return new \Mockery\Matcher\HasValue($val);
373
    }
374
375
    /**
376
     * Return instance of CLOSURE matcher.
377
     *
378
     * @param $closure
379
     *
380
     * @return \Mockery\Matcher\Closure
381
     */
382 6
    public static function on($closure)
383
    {
384 6
        return new \Mockery\Matcher\Closure($closure);
385
    }
386
387
    /**
388
     * Return instance of MUSTBE matcher.
389
     *
390
     * @param $expected
391
     *
392
     * @return \Mockery\Matcher\MustBe
393
     */
394 6
    public static function mustBe($expected)
395
    {
396 6
        return new \Mockery\Matcher\MustBe($expected);
0 ignored issues
show
Deprecated Code introduced by
The class Mockery\Matcher\MustBe has been deprecated with message: 2.0 Due to ambiguity, use Hamcrest or PHPUnit equivalents

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
397
    }
398
399
    /**
400
     * Return instance of NOT matcher.
401
     *
402
     * @param $expected
403
     *
404
     * @return \Mockery\Matcher\Not
405
     */
406 3
    public static function not($expected)
407
    {
408 3
        return new \Mockery\Matcher\Not($expected);
409
    }
410
411
    /**
412
     * Return instance of ANYOF matcher.
413
     *
414
     * @param array $args
415
     *
416
     * @return \Mockery\Matcher\AnyOf
417
     */
418 3
    public static function anyOf(...$args)
419
    {
420 3
        return new \Mockery\Matcher\AnyOf($args);
421
    }
422
423
    /**
424
     * Return instance of NOTANYOF matcher.
425
     *
426
     * @param array $args
427
     *
428
     * @return \Mockery\Matcher\NotAnyOf
429
     */
430 3
    public static function notAnyOf(...$args)
431
    {
432 3
        return new \Mockery\Matcher\NotAnyOf($args);
433
    }
434
435
    /**
436
     * Lazy loader and Getter for the global
437
     * configuration container.
438
     *
439
     * @return \Mockery\Configuration
440
     */
441 486
    public static function getConfiguration()
442
    {
443 486
        if (is_null(self::$_config)) {
444 1
            self::$_config = new \Mockery\Configuration();
445
        }
446
447 486
        return self::$_config;
448
    }
449
450
    /**
451
     * Utility method to format method name and arguments into a string.
452
     *
453
     * @param string $method
454
     * @param array $arguments
455
     *
456
     * @return string
457
     */
458 102
    public static function formatArgs($method, array $arguments = null)
459
    {
460 102
        if (is_null($arguments)) {
461
            return $method . '()';
462
        }
463
464 102
        $formattedArguments = array();
465 102
        foreach ($arguments as $argument) {
466 101
            $formattedArguments[] = self::formatArgument($argument);
467
        }
468
469 102
        return $method . '(' . implode(', ', $formattedArguments) . ')';
470
    }
471
472
    /**
473
     * Gets the string representation
474
     * of any passed argument.
475
     *
476
     * @param $argument
477
     * @param $depth
478
     *
479
     * @return string
480
     */
481 101
    private static function formatArgument($argument, $depth = 0)
482
    {
483 101
        if ($argument instanceOf MatcherAbstract) {
484 41
            return (string) $argument;
485
        }
486
487 60
        if (is_object($argument)) {
488 7
            return 'object(' . get_class($argument) . ')';
489
        }
490
491 57
        if (is_int($argument) || is_float($argument)) {
492 40
            return $argument;
493
        }
494
495 26
        if (is_array($argument)) {
496 13
            if ($depth === 1) {
497 2
                $argument = '[...]';
498
            } else {
499 13
                $sample = array();
500 13
                foreach ($argument as $key => $value) {
501 12
                    $key = is_int($key) ? $key : "'$key'";
502 12
                    $value = self::formatArgument($value, $depth + 1);
503 12
                    $sample[] = "$key => $value";
504
                }
505
506 13
                $argument = "[".implode(", ", $sample)."]";
507
            }
508
509 13
            return ((strlen($argument) > 1000) ? substr($argument, 0, 1000).'...]' : $argument);
510
        }
511
512 17
        if (is_bool($argument)) {
513 1
            return $argument ? 'true' : 'false';
514
        }
515
516 16
        if (is_resource($argument)) {
517 2
            return 'resource(...)';
518
        }
519
520 14
        if (is_null($argument)) {
521 1
            return 'NULL';
522
        }
523
524 13
        return "'".(string) $argument."'";
525
    }
526
527
    /**
528
     * Utility function to format objects to printable arrays.
529
     *
530
     * @param array $objects
531
     *
532
     * @return string
533
     */
534 52
    public static function formatObjects(array $objects = null)
535
    {
536 52
        static $formatting;
537
538 52
        if ($formatting) {
539 1
            return '[Recursion]';
540
        }
541
542 52
        if (is_null($objects)) {
543
            return '';
544
        }
545
546 52
        $objects = array_filter($objects, 'is_object');
547 52
        if (empty($objects)) {
548 45
            return '';
549
        }
550
551 7
        $formatting = true;
552 7
        $parts = array();
553
554 7
        foreach ($objects as $object) {
555 7
            $parts[get_class($object)] = self::objectToArray($object);
556
        }
557
558 7
        $formatting = false;
559
560 7
        return 'Objects: ( ' . var_export($parts, true) . ')';
561
    }
562
563
    /**
564
     * Utility function to turn public properties and public get* and is* method values into an array.
565
     *
566
     * @param     $object
567
     * @param int $nesting
568
     *
569
     * @return array
570
     */
571 7
    private static function objectToArray($object, $nesting = 3)
572
    {
573 7
        if ($nesting == 0) {
574
            return array('...');
575
        }
576
577
        return array(
578 7
            'class' => get_class($object),
579 7
            'properties' => self::extractInstancePublicProperties($object, $nesting),
580 7
            'getters' => self::extractGetters($object, $nesting)
581
        );
582
    }
583
584
    /**
585
     * Returns all public instance properties.
586
     *
587
     * @param $object
588
     * @param $nesting
589
     *
590
     * @return array
591
     */
592 7
    private static function extractInstancePublicProperties($object, $nesting)
593
    {
594 7
        $reflection = new \ReflectionClass(get_class($object));
595 7
        $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
596 7
        $cleanedProperties = array();
597
598 7
        foreach ($properties as $publicProperty) {
599 1
            if (!$publicProperty->isStatic()) {
600
                $name = $publicProperty->getName();
601
                $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);
602
            }
603
        }
604
605 7
        return $cleanedProperties;
606
    }
607
608
    /**
609
     * Returns all object getters.
610
     *
611
     * @param $object
612
     * @param $nesting
613
     *
614
     * @return array
615
     */
616 7
    private static function extractGetters($object, $nesting)
617
    {
618 7
        $reflection = new \ReflectionClass(get_class($object));
619 7
        $publicMethods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
620 7
        $getters = array();
621
622 7
        foreach ($publicMethods as $publicMethod) {
623 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...
624 5
            $irrelevantName = (substr($name, 0, 3) !== 'get' && substr($name, 0, 2) !== 'is');
625 5
            $isStatic = $publicMethod->isStatic();
626 5
            $numberOfParameters = $publicMethod->getNumberOfParameters();
627
628 5
            if ($irrelevantName || $numberOfParameters != 0 || $isStatic) {
629 5
                continue;
630
            }
631
632
            try {
633 2
                $getters[$name] = self::cleanupNesting($object->$name(), $nesting);
634 1
            } catch (\Exception $e) {
635 1
                $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
636
            }
637
        }
638
639 7
        return $getters;
640
    }
641
642
    /**
643
     * Utility method used for recursively generating
644
     * an object or array representation.
645
     *
646
     * @param $argument
647
     * @param $nesting
648
     *
649
     * @return mixed
650
     */
651 1
    private static function cleanupNesting($argument, $nesting)
652
    {
653 1
        if (is_object($argument)) {
654
            $object = self::objectToArray($argument, $nesting - 1);
655
            $object['class'] = get_class($argument);
656
657
            return $object;
658
        }
659
660 1
        if (is_array($argument)) {
661 1
            return self::cleanupArray($argument, $nesting - 1);
662
        }
663
664 1
        return $argument;
665
    }
666
667
    /**
668
     * Utility method for recursively
669
     * gerating a representation
670
     * of the given array.
671
     *
672
     * @param array $argument
673
     * @param int $nesting
674
     *
675
     * @return mixed
676
     */
677 1
    private static function cleanupArray($argument, $nesting = 3)
678
    {
679 1
        if ($nesting == 0) {
680 1
            return '...';
681
        }
682
683 1
        foreach ($argument as $key => $value) {
684 1
            if (is_array($value)) {
685 1
                $argument[$key] = self::cleanupArray($value, $nesting - 1);
686
            } elseif (is_object($value)) {
687
                $argument[$key] = self::objectToArray($value, $nesting - 1);
688
            }
689
        }
690
691 1
        return $argument;
692
    }
693
694
    /**
695
     * Utility function to parse shouldReceive() arguments and generate
696
     * expectations from such as needed.
697
     *
698
     * @param Mockery\MockInterface $mock
699
     * @param array $args
700
     * @param callable $add
701
     * @return \Mockery\CompositeExpectation
702
     */
703 342
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
704
    {
705 342
        $composite = new \Mockery\CompositeExpectation();
706
707 342
        foreach ($args as $arg) {
708 342
            if (is_array($arg)) {
709 18
                foreach ($arg as $k => $v) {
710 18
                    $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...
711 18
                    $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...
712
                }
713
            } elseif (is_string($arg)) {
714 328
                $expectation = self::buildDemeterChain($mock, $arg, $add);
715 322
                $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...
716
            }
717
        }
718
719 336
        return $composite;
720
    }
721
722
    /**
723
     * Sets up expectations on the members of the CompositeExpectation and
724
     * builds up any demeter chain that was passed to shouldReceive.
725
     *
726
     * @param \Mockery\MockInterface $mock
727
     * @param string $arg
728
     * @param callable $add
729
     * @throws Mockery\Exception
730
     * @return \Mockery\ExpectationInterface
731
     */
732 342
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
733
    {
734
        /** @var Mockery\Container $container */
735 342
        $container = $mock->mockery_getContainer();
736 342
        $methodNames = explode('->', $arg);
737 342
        reset($methodNames);
738
739 342
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
740 7
            && !$mock->mockery_isAnonymous()
741 6
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
742
        ) {
743 4
            throw new \Mockery\Exception(
744
                'Mockery\'s configuration currently forbids mocking the method '
745 4
                . current($methodNames) . ' as it does not exist on the class or object '
746 4
                . 'being mocked'
747
            );
748
        }
749
750
        /** @var ExpectationInterface|null $expectations */
751 338
        $expectations = null;
752
753
        /** @var Callable $nextExp */
754
        $nextExp = function ($method) use ($add) {
755 338
            return $add($method);
756 338
        };
757
758 338
        while (true) {
759 338
            $method = array_shift($methodNames);
760 338
            $expectations = $mock->mockery_getExpectationsFor($method);
761
762 338
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
763 338
                $expectations = $nextExp($method);
764 336
                if (self::noMoreElementsInChain($methodNames)) {
765 336
                    break;
766
                }
767
768 11
                $mock = self::getNewDemeterMock($container, $method, $expectations);
769
            } else {
770 5
                $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
771 5
                if ($demeterMockKey) {
772 5
                    $mock = self::getExistingDemeterMock($container, $demeterMockKey);
773
                }
774
            }
775
776 11
            $nextExp = function ($n) use ($mock) {
777 11
                return $mock->shouldReceive($n);
778 11
            };
779
        }
780
781 336
        return $expectations;
782
    }
783
784
    /**
785
     * Gets a new demeter configured
786
     * mock from the container.
787
     *
788
     * @param \Mockery\Container $container
789
     * @param string $method
790
     * @param Mockery\ExpectationInterface $exp
791
     *
792
     * @return \Mockery\Mock
793
     */
794 11
    private static function getNewDemeterMock(
795
        Mockery\Container $container,
796
        $method,
797
        Mockery\ExpectationInterface $exp
798
    ) {
799 11
        $mock = $container->mock('demeter_' . $method);
800 11
        $exp->andReturn($mock);
801
802 11
        return $mock;
803
    }
804
805
    /**
806
     * Gets an specific demeter mock from
807
     * the ones kept by the container.
808
     *
809
     * @param \Mockery\Container $container
810
     * @param string $demeterMockKey
811
     *
812
     * @return mixed
813
     */
814 5
    private static function getExistingDemeterMock(
815
        Mockery\Container $container,
816
        $demeterMockKey
817
    ) {
818 5
        $mocks = $container->getMocks();
819 5
        $mock = $mocks[$demeterMockKey];
820
821 5
        return $mock;
822
    }
823
824
    /**
825
     * Checks if the passed array representing a demeter
826
     * chain with the method names is empty.
827
     *
828
     * @param array $methodNames
829
     *
830
     * @return bool
831
     */
832 336
    private static function noMoreElementsInChain(array $methodNames)
833
    {
834 336
        return empty($methodNames);
835
    }
836
837 17
    public static function declareClass($fqn)
838
    {
839 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...
840
    }
841
842 2
    public static function declareInterface($fqn)
843
    {
844 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...
845
    }
846
847 18
    private static function declareType($fqn, $type)
848
    {
849 18
        $targetCode = "<?php ";
850 18
        $shortName = $fqn;
851
852 18
        if (strpos($fqn, "\\")) {
853 4
            $parts = explode("\\", $fqn);
854
855 4
            $shortName = trim(array_pop($parts));
856 4
            $namespace = implode("\\", $parts);
857
858 4
            $targetCode.= "namespace $namespace;\n";
859
        }
860
861 18
        $targetCode.= "$type $shortName {} ";
862
863
        /*
864
         * We could eval here, but it doesn't play well with the way
865
         * PHPUnit tries to backup global state and the require definition
866
         * loader
867
         */
868 18
        $tmpfname = tempnam(sys_get_temp_dir(), "Mockery");
869 18
        file_put_contents($tmpfname, $targetCode);
870 18
        require $tmpfname;
871 18
        \Mockery::registerFileForCleanUp($tmpfname);
872 18
    }
873
874
    /**
875
     * Register a file to be deleted on tearDown.
876
     *
877
     * @param string $fileName
878
     */
879 18
    public static function registerFileForCleanUp($fileName)
880
    {
881 18
        self::$_filesToCleanUp[] = $fileName;
882 18
    }
883
}
884