Completed
Pull Request — master (#682)
by Bilge
02:41
created

Mockery::objectToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
crap 2.0185
1
<?php
2
/**
3
 * Mockery
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://github.com/padraic/mockery/blob/master/LICENSE
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Mockery
16
 * @package    Mockery
17
 * @copyright  Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
18
 * @license    http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
19
 */
20
21
use Mockery\ExpectationInterface;
22
use Mockery\Generator\CachingGenerator;
23
use Mockery\Generator\Generator;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Generator.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
24
use Mockery\Generator\MockConfigurationBuilder;
25
use Mockery\Generator\StringManipulation\Pass\RemoveDestructorPass;
26
use Mockery\Generator\StringManipulationGenerator;
27
use Mockery\Generator\StringManipulation\Pass\CallTypeHintPass;
28
use Mockery\Generator\StringManipulation\Pass\MagicMethodTypeHintsPass;
29
use Mockery\Generator\StringManipulation\Pass\ClassNamePass;
30
use Mockery\Generator\StringManipulation\Pass\ClassPass;
31
use Mockery\Generator\StringManipulation\Pass\InstanceMockPass;
32
use Mockery\Generator\StringManipulation\Pass\InterfacePass;
33
use Mockery\Generator\StringManipulation\Pass\MethodDefinitionPass;
34
use Mockery\Generator\StringManipulation\Pass\RemoveBuiltinMethodsThatAreFinalPass;
35
use Mockery\Generator\StringManipulation\Pass\RemoveUnserializeForInternalSerializableClassesPass;
36
use Mockery\Loader\EvalLoader;
37
use Mockery\Loader\Loader;
38
39
class Mockery
40
{
41
    const BLOCKS = 'Mockery_Forward_Blocks';
42
43
    /**
44
     * Global container to hold all mocks for the current unit test running.
45
     *
46
     * @var \Mockery\Container
47
     */
48
    protected static $_container = null;
49
50
    /**
51
     * Global configuration handler containing configuration options.
52
     *
53
     * @var \Mockery\Configuration
54
     */
55
    protected static $_config = null;
56
57
    /**
58
     * @var \Mockery\Generator\Generator
59
     */
60
    protected static $_generator;
61
62
    /**
63
     * @var \Mockery\Loader\Loader
64
     */
65
    protected static $_loader;
66
67
    /**
68
     * @var array
69
     */
70
    private static $_filesToCleanUp = [];
71
72
    /**
73
     * Static shortcut to \Mockery\Container::mock().
74
     *
75
     * @return \Mockery\MockInterface
76
     */
77 45
    public static function mock()
78
    {
79 45
        $args = func_get_args();
80
81 45
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
82
    }
83
84
    /**
85
     * Static and semantic shortcut for getting a mock from the container
86
     * and applying the spy's expected behavior into it.
87
     *
88
     * @return \Mockery\MockInterface
89
     */
90 8
    public static function spy()
91
    {
92 8
        $args = func_get_args();
93 8
        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
94
    }
95
96
    /**
97
     * Static and Semantic shortcut to \Mockery\Container::mock().
98
     *
99
     * @return \Mockery\MockInterface
100
     */
101
    public static function instanceMock()
102
    {
103
        $args = func_get_args();
104
105
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
106
    }
107
108
    /**
109
     * Static shortcut to \Mockery\Container::mock(), first argument names the mock.
110
     *
111
     * @return \Mockery\MockInterface
112
     */
113 4
    public static function namedMock()
114
    {
115 4
        $args = func_get_args();
116 4
        $name = array_shift($args);
117
118 4
        $builder = new MockConfigurationBuilder();
119 4
        $builder->setName($name);
120
121 4
        array_unshift($args, $builder);
122
123 4
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
124
    }
125
126
    /**
127
     * Static shortcut to \Mockery\Container::self().
128
     *
129
     * @throws LogicException
130
     *
131
     * @return \Mockery\MockInterface
132
     */
133 2
    public static function self()
134
    {
135 2
        if (is_null(self::$_container)) {
136 1
            throw new \LogicException('You have not declared any mocks yet');
137
        }
138
139 1
        return self::$_container->self();
140
    }
141
142
    /**
143
     * Static shortcut to closing up and verifying all mocks in the global
144
     * container, and resetting the container static variable to null.
145
     *
146
     * @return void
147
     */
148 432
    public static function close()
149
    {
150 432
        foreach (self::$_filesToCleanUp as $fileName) {
151 18
            @unlink($fileName);
152
        }
153 432
        self::$_filesToCleanUp = [];
154
155 432
        if (is_null(self::$_container)) {
156
            return;
157
        }
158
159 432
        self::$_container->mockery_teardown();
160 432
        self::$_container->mockery_close();
161 432
        self::$_container = null;
162 432
    }
163
164
    /**
165
     * Static fetching of a mock associated with a name or explicit class poser.
166
     *
167
     * @param $name
168
     *
169
     * @return \Mockery\Mock
170
     */
171 11
    public static function fetchMock($name)
172
    {
173 11
        return self::$_container->fetchMock($name);
174
    }
175
176
    /**
177
     * Lazy loader and getter for
178
     * the container property.
179
     *
180
     * @return Mockery\Container
181
     */
182 461
    public static function getContainer()
183
    {
184 461
        if (is_null(self::$_container)) {
185 427
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
186
        }
187
188 461
        return self::$_container;
189
    }
190
191
    /**
192
     * Setter for the $_generator static propery.
193
     *
194
     * @param \Mockery\Generator\Generator $generator
195
     */
196
    public static function setGenerator(Generator $generator)
197
    {
198
        self::$_generator = $generator;
199
    }
200
201
    /**
202
     * Lazy loader method and getter for
203
     * the generator property.
204
     *
205
     * @return Generator
206
     */
207 427
    public static function getGenerator()
208
    {
209 427
        if (is_null(self::$_generator)) {
210 1
            self::$_generator = self::getDefaultGenerator();
211
        }
212
213 427
        return self::$_generator;
214
    }
215
216
    /**
217
     * Creates and returns a default generator
218
     * used inside this class.
219
     *
220
     * @return CachingGenerator
221
     */
222 427
    public static function getDefaultGenerator()
223
    {
224 427
        $generator = new StringManipulationGenerator(array(
225 427
            new CallTypeHintPass(),
226 427
            new MagicMethodTypeHintsPass(),
227 427
            new ClassPass(),
228 427
            new ClassNamePass(),
229 427
            new InstanceMockPass(),
230 427
            new InterfacePass(),
231 427
            new MethodDefinitionPass(),
232 427
            new RemoveUnserializeForInternalSerializableClassesPass(),
233 427
            new RemoveBuiltinMethodsThatAreFinalPass(),
234 427
            new RemoveDestructorPass(),
235
        ));
236
237 427
        return new CachingGenerator($generator);
238
    }
239
240
    /**
241
     * Setter for the $_loader static property.
242
     *
243
     * @param Loader $loader
244
     */
245
    public static function setLoader(Loader $loader)
246
    {
247
        self::$_loader = $loader;
248
    }
249
250
    /**
251
     * Lazy loader method and getter for
252
     * the $_loader property.
253
     *
254
     * @return Loader
255
     */
256 427
    public static function getLoader()
257
    {
258 427
        if (is_null(self::$_loader)) {
259 1
            self::$_loader = self::getDefaultLoader();
260
        }
261
262 427
        return self::$_loader;
263
    }
264
265
    /**
266
     * Gets an EvalLoader to be used as default.
267
     *
268
     * @return EvalLoader
269
     */
270 302
    public static function getDefaultLoader()
271
    {
272 302
        return new EvalLoader();
273
    }
274
275
    /**
276
     * Set the container.
277
     *
278
     * @param \Mockery\Container $container
279
     *
280
     * @return \Mockery\Container
281
     */
282 18
    public static function setContainer(Mockery\Container $container)
283
    {
284 18
        return self::$_container = $container;
285
    }
286
287
    /**
288
     * Reset the container to null.
289
     *
290
     * @return void
291
     */
292 14
    public static function resetContainer()
293
    {
294 14
        self::$_container = null;
295 14
    }
296
297
    /**
298
     * Return instance of ANY matcher.
299
     *
300
     * @return \Mockery\Matcher\Any
301
     */
302 5
    public static function any()
303
    {
304 5
        return new \Mockery\Matcher\Any();
305
    }
306
307
    /**
308
     * Return instance of TYPE matcher.
309
     *
310
     * @param $expected
311
     *
312
     * @return \Mockery\Matcher\Type
313
     */
314 47
    public static function type($expected)
315
    {
316 47
        return new \Mockery\Matcher\Type($expected);
317
    }
318
319
    /**
320
     * Return instance of DUCKTYPE matcher.
321
     *
322
     * @return \Mockery\Matcher\Ducktype
323
     */
324 3
    public static function ducktype()
325
    {
326 3
        return new \Mockery\Matcher\Ducktype(func_get_args());
327
    }
328
329
    /**
330
     * Return instance of SUBSET matcher.
331
     *
332
     * @param array $part
333
     *
334
     * @return \Mockery\Matcher\Subset
335
     */
336 3
    public static function subset(array $part)
337
    {
338 3
        return new \Mockery\Matcher\Subset($part);
339
    }
340
341
    /**
342
     * Return instance of CONTAINS matcher.
343
     *
344
     * @return \Mockery\Matcher\Contains
345
     */
346 3
    public static function contains()
347
    {
348 3
        return new \Mockery\Matcher\Contains(func_get_args());
349
    }
350
351
    /**
352
     * Return instance of HASKEY matcher.
353
     *
354
     * @param $key
355
     *
356
     * @return \Mockery\Matcher\HasKey
357
     */
358 3
    public static function hasKey($key)
359
    {
360 3
        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);
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
     * @return \Mockery\Matcher\AnyOf
415
     */
416 3
    public static function anyOf()
417
    {
418 3
        return new \Mockery\Matcher\AnyOf(func_get_args());
419
    }
420
421
    /**
422
     * Return instance of NOTANYOF matcher.
423
     *
424
     * @return \Mockery\Matcher\NotAnyOf
425
     */
426 3
    public static function notAnyOf()
427
    {
428 3
        return new \Mockery\Matcher\NotAnyOf(func_get_args());
429
    }
430
431
    /**
432
     * Lazy loader and Getter for the global
433
     * configuration container.
434
     *
435
     * @return \Mockery\Configuration
436
     */
437 454
    public static function getConfiguration()
438
    {
439 454
        if (is_null(self::$_config)) {
440 1
            self::$_config = new \Mockery\Configuration();
441
        }
442
443 454
        return self::$_config;
444
    }
445
446
    /**
447
     * Utility method to format method name and arguments into a string.
448
     *
449
     * @param string $method
450
     * @param array $arguments
451
     *
452
     * @return string
453
     */
454 90
    public static function formatArgs($method, array $arguments = null)
455
    {
456 90
        if (is_null($arguments)) {
457
            return $method . '()';
458
        }
459
460 90
        $formattedArguments = array();
461 90
        foreach ($arguments as $argument) {
462 56
            $formattedArguments[] = self::formatArgument($argument);
463
        }
464
465 90
        return $method . '(' . implode(', ', $formattedArguments) . ')';
466
    }
467
468
    /**
469
     * Gets the string representation
470
     * of any passed argument.
471
     *
472
     * @param $argument
473
     * @param $depth
474
     *
475
     * @return string
476
     */
477 56
    private static function formatArgument($argument, $depth = 0)
478
    {
479 56
        if (is_object($argument)) {
480 8
            return 'object(' . get_class($argument) . ')';
481
        }
482
483 52
        if (is_int($argument) || is_float($argument)) {
484 35
            return $argument;
485
        }
486
487 26
        if (is_array($argument)) {
488 13
            if ($depth === 1) {
489 2
                $argument = '[...]';
490
            } else {
491 13
                $sample = array();
492 13
                foreach ($argument as $key => $value) {
493 12
                    $key = is_int($key) ? $key : "'$key'";
494 12
                    $value = self::formatArgument($value, $depth + 1);
495 12
                    $sample[] = "$key => $value";
496
                }
497
498 13
                $argument = "[".implode(", ", $sample)."]";
499
            }
500
501 13
            return ((strlen($argument) > 1000) ? substr($argument, 0, 1000).'...]' : $argument);
502
        }
503
504 17
        if (is_bool($argument)) {
505 1
            return $argument ? 'true' : 'false';
506
        }
507
508 16
        if (is_resource($argument)) {
509 2
            return 'resource(...)';
510
        }
511
512 14
        if (is_null($argument)) {
513 1
            return 'NULL';
514
        }
515
516 13
        return "'".(string) $argument."'";
517
    }
518
519
    /**
520
     * Utility function to format objects to printable arrays.
521
     *
522
     * @param array $objects
523
     *
524
     * @return string
525
     */
526 52
    public static function formatObjects(array $objects = null)
527
    {
528 52
        static $formatting;
529
530 52
        if ($formatting) {
531 1
            return '[Recursion]';
532
        }
533
534 52
        if (is_null($objects)) {
535
            return '';
536
        }
537
538 52
        $objects = array_filter($objects, 'is_object');
539 52
        if (empty($objects)) {
540 45
            return '';
541
        }
542
543 7
        $formatting = true;
544 7
        $parts = array();
545
546 7
        foreach ($objects as $object) {
547 7
            $parts[get_class($object)] = self::objectToArray($object);
548
        }
549
550 7
        $formatting = false;
551
552 7
        return 'Objects: ( ' . var_export($parts, true) . ')';
553
    }
554
555
    /**
556
     * Utility function to turn public properties and public get* and is* method values into an array.
557
     *
558
     * @param     $object
559
     * @param int $nesting
560
     *
561
     * @return array
562
     */
563 7
    private static function objectToArray($object, $nesting = 3)
564
    {
565 7
        if ($nesting == 0) {
566
            return array('...');
567
        }
568
569
        return array(
570 7
            'class' => get_class($object),
571 7
            'properties' => self::extractInstancePublicProperties($object, $nesting),
572 7
            'getters' => self::extractGetters($object, $nesting)
573
        );
574
    }
575
576
    /**
577
     * Returns all public instance properties.
578
     *
579
     * @param $object
580
     * @param $nesting
581
     *
582
     * @return array
583
     */
584 7
    private static function extractInstancePublicProperties($object, $nesting)
585
    {
586 7
        $reflection = new \ReflectionClass(get_class($object));
587 7
        $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
588 7
        $cleanedProperties = array();
589
590 7
        foreach ($properties as $publicProperty) {
591 1
            if (!$publicProperty->isStatic()) {
592
                $name = $publicProperty->getName();
593 1
                $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);
594
            }
595
        }
596
597 7
        return $cleanedProperties;
598
    }
599
600
    /**
601
     * Returns all object getters.
602
     *
603
     * @param $object
604
     * @param $nesting
605
     *
606
     * @return array
607
     */
608 7
    private static function extractGetters($object, $nesting)
609
    {
610 7
        $reflection = new \ReflectionClass(get_class($object));
611 7
        $publicMethods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
612 7
        $getters = array();
613
614 7
        foreach ($publicMethods as $publicMethod) {
615 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...
616 5
            $irrelevantName = (substr($name, 0, 3) !== 'get' && substr($name, 0, 2) !== 'is');
617 5
            $isStatic = $publicMethod->isStatic();
618 5
            $numberOfParameters = $publicMethod->getNumberOfParameters();
619
620 5
            if ($irrelevantName || $numberOfParameters != 0 || $isStatic) {
621 5
                continue;
622
            }
623
624
            try {
625 2
                $getters[$name] = self::cleanupNesting($object->$name(), $nesting);
626 1
            } catch (\Exception $e) {
627 2
                $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
628
            }
629
        }
630
631 7
        return $getters;
632
    }
633
634
    /**
635
     * Utility method used for recursively generating
636
     * an object or array representation.
637
     *
638
     * @param $argument
639
     * @param $nesting
640
     *
641
     * @return mixed
642
     */
643 1
    private static function cleanupNesting($argument, $nesting)
644
    {
645 1
        if (is_object($argument)) {
646
            $object = self::objectToArray($argument, $nesting - 1);
647
            $object['class'] = get_class($argument);
648
649
            return $object;
650
        }
651
652 1
        if (is_array($argument)) {
653 1
            return self::cleanupArray($argument, $nesting - 1);
654
        }
655
656 1
        return $argument;
657
    }
658
659
    /**
660
     * Utility method for recursively
661
     * gerating a representation
662
     * of the given array.
663
     *
664
     * @param array $argument
665
     * @param int $nesting
666
     *
667
     * @return mixed
668
     */
669 1
    private static function cleanupArray($argument, $nesting = 3)
670
    {
671 1
        if ($nesting == 0) {
672 1
            return '...';
673
        }
674
675 1
        foreach ($argument as $key => $value) {
676 1
            if (is_array($value)) {
677 1
                $argument[$key] = self::cleanupArray($value, $nesting - 1);
678
            } elseif (is_object($value)) {
679 1
                $argument[$key] = self::objectToArray($value, $nesting - 1);
680
            }
681
        }
682
683 1
        return $argument;
684
    }
685
686
    /**
687
     * Utility function to parse shouldReceive() arguments and generate
688
     * expectations from such as needed.
689
     *
690
     * @param Mockery\MockInterface $mock
691
     * @param array $args
692
     * @param callable $add
693
     * @return \Mockery\CompositeExpectation
694
     */
695 329
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
696
    {
697 329
        $composite = new \Mockery\CompositeExpectation();
698
699 329
        foreach ($args as $arg) {
700 329
            if (is_array($arg)) {
701 16
                foreach ($arg as $k => $v) {
702 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...
703 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...
704
                }
705
            } elseif (is_string($arg)) {
706 317
                $expectation = self::buildDemeterChain($mock, $arg, $add);
707 323
                $composite->add($expectation);
0 ignored issues
show
Documentation introduced by
$expectation is of type object<Mockery\ExpectationInterface>, but the function expects a object<Mockery\Expectati...y\CompositeExpectation>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
708
            }
709
        }
710
711 323
        return $composite;
712
    }
713
714
    /**
715
     * Sets up expectations on the members of the CompositeExpectation and
716
     * builds up any demeter chain that was passed to shouldReceive.
717
     *
718
     * @param \Mockery\MockInterface $mock
719
     * @param string $arg
720
     * @param callable $add
721
     * @throws Mockery\Exception
722
     * @return \Mockery\ExpectationInterface
723
     */
724 329
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
725
    {
726
        /** @var Mockery\Container $container */
727 329
        $container = $mock->mockery_getContainer();
728 329
        $methodNames = explode('->', $arg);
729 329
        reset($methodNames);
730
731 329
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
732 329
            && !$mock->mockery_isAnonymous()
733 329
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
734
        ) {
735 4
            throw new \Mockery\Exception(
736
                'Mockery\'s configuration currently forbids mocking the method '
737 4
                . current($methodNames) . ' as it does not exist on the class or object '
738 4
                . 'being mocked'
739
            );
740
        }
741
742
        /** @var ExpectationInterface|null $expectations */
743 325
        $expectations = null;
744
745
        /** @var Callable $nextExp */
746
        $nextExp = function ($method) use ($add) {
747 325
            return $add($method);
748 325
        };
749
750 325
        while (true) {
751 325
            $method = array_shift($methodNames);
752 325
            $expectations = $mock->mockery_getExpectationsFor($method);
753
754 325
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
755 325
                $expectations = $nextExp($method);
756 323
                if (self::noMoreElementsInChain($methodNames)) {
757 323
                    break;
758
                }
759
760 11
                $mock = self::getNewDemeterMock($container, $method, $expectations);
761
            } else {
762 5
                $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
763 5
                if ($demeterMockKey) {
764 5
                    $mock = self::getExistingDemeterMock($container, $demeterMockKey);
765
                }
766
            }
767
768 11
            $nextExp = function ($n) use ($mock) {
769 11
                return $mock->shouldReceive($n);
770 11
            };
771
        }
772
773 323
        return $expectations;
774
    }
775
776
    /**
777
     * Gets a new demeter configured
778
     * mock from the container.
779
     *
780
     * @param \Mockery\Container $container
781
     * @param string $method
782
     * @param Mockery\ExpectationInterface $exp
783
     *
784
     * @return \Mockery\Mock
785
     */
786 11
    private static function getNewDemeterMock(
787
        Mockery\Container $container,
788
        $method,
789
        Mockery\ExpectationInterface $exp
790
    ) {
791 11
        $mock = $container->mock('demeter_' . $method);
792 11
        $exp->andReturn($mock);
793
794 11
        return $mock;
795
    }
796
797
    /**
798
     * Gets an specific demeter mock from
799
     * the ones kept by the container.
800
     *
801
     * @param \Mockery\Container $container
802
     * @param string $demeterMockKey
803
     *
804
     * @return mixed
805
     */
806 5
    private static function getExistingDemeterMock(
807
        Mockery\Container $container,
808
        $demeterMockKey
809
    ) {
810 5
        $mocks = $container->getMocks();
811 5
        $mock = $mocks[$demeterMockKey];
812
813 5
        return $mock;
814
    }
815
816
    /**
817
     * Checks if the passed array representing a demeter
818
     * chain with the method names is empty.
819
     *
820
     * @param array $methodNames
821
     *
822
     * @return bool
823
     */
824 323
    private static function noMoreElementsInChain(array $methodNames)
825
    {
826 323
        return empty($methodNames);
827
    }
828
829 17
    public static function declareClass($fqn)
830
    {
831 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...
832
    }
833
834 2
    public static function declareInterface($fqn)
835
    {
836 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...
837
    }
838
839 18
    private static function declareType($fqn, $type)
840
    {
841 18
        $targetCode = "<?php ";
842 18
        $shortName = $fqn;
843
844 18
        if (strpos($fqn, "\\")) {
845 4
            $parts = explode("\\", $fqn);
846
847 4
            $shortName = trim(array_pop($parts));
848 4
            $namespace = implode("\\", $parts);
849
850 4
            $targetCode.= "namespace $namespace;\n";
851
        }
852
853 18
        $targetCode.= "$type $shortName {} ";
854
855
        /*
856
         * We could eval here, but it doesn't play well with the way
857
         * PHPUnit tries to backup global state and the require definition
858
         * loader
859
         */
860 18
        $tmpfname = tempnam(sys_get_temp_dir(), "Mockery");
861 18
        file_put_contents($tmpfname, $targetCode);
862 18
        require $tmpfname;
863 18
        \Mockery::registerFileForCleanUp($tmpfname);
864 18
    }
865
866
    /**
867
     * Register a file to be deleted on tearDown.
868
     *
869
     * @param string $fileName
870
     */
871 18
    public static function registerFileForCleanUp($fileName)
872
    {
873 18
        self::$_filesToCleanUp[] = $fileName;
874 18
    }
875
}
876