Completed
Pull Request — master (#684)
by Dave
02:32
created

Mockery::formatArgs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

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