Completed
Pull Request — master (#684)
by Dave
03:09
created

Mockery::globalHelpers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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