Completed
Pull Request — master (#712)
by Dave
03: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\StringManipulationGenerator;
26
use Mockery\Loader\EvalLoader;
27
use Mockery\Loader\Loader;
28
use Mockery\Matcher\MatcherAbstract;
29
use Mockery\ClosureWrapper;
30
31
class Mockery
32
{
33
    const BLOCKS = 'Mockery_Forward_Blocks';
34
35
    /**
36
     * Global container to hold all mocks for the current unit test running.
37
     *
38
     * @var \Mockery\Container
39
     */
40
    protected static $_container = null;
41
42
    /**
43
     * Global configuration handler containing configuration options.
44
     *
45
     * @var \Mockery\Configuration
46
     */
47
    protected static $_config = null;
48
49
    /**
50
     * @var \Mockery\Generator\Generator
51
     */
52
    protected static $_generator;
53
54
    /**
55
     * @var \Mockery\Loader\Loader
56
     */
57
    protected static $_loader;
58
59
    /**
60
     * @var array
61
     */
62
    private static $_filesToCleanUp = [];
63
64
    /**
65
     * Defines the global helper functions
66
     * 
67
     * @return void
68
     */
69 3
    public static function globalHelpers()
70
    {
71 3
        require_once __DIR__.'/helpers.php';
72 3
    }
73
74
    /**
75
     * Static shortcut to \Mockery\Container::mock().
76
     *
77
     * @param array $args
78
     *
79
     * @return \Mockery\MockInterface
80
     */
81 60
    public static function mock(...$args)
82
    {
83 60
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
84
    }
85
86
    /**
87
     * Static and semantic shortcut for getting a mock from the container
88
     * and applying the spy's expected behavior into it.
89
     *
90
     * @param array $args
91
     *
92
     * @return \Mockery\MockInterface
93
     */
94 26
    public static function spy(...$args)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
95
    {
96 26
        $args = func_get_args();
97
98 26
        if (count($args) && $args[0] instanceof \Closure) {
99 16
            $args[0] = new ClosureWrapper($args[0]);
100
        }
101
102 26
        return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
103
    }
104
105
    /**
106
     * Static and Semantic shortcut to \Mockery\Container::mock().
107
     *
108
     * @param array $args
109
     *
110
     * @return \Mockery\MockInterface
111
     */
112
    public static function instanceMock(...$args)
113
    {
114
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
115
    }
116
117
    /**
118
     * Static shortcut to \Mockery\Container::mock(), first argument names the mock.
119
     *
120
     * @param array $args
121
     *
122
     * @return \Mockery\MockInterface
123
     */
124 5
    public static function namedMock(...$args)
125
    {
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 460
    public static function close()
159
    {
160 460
        foreach (self::$_filesToCleanUp as $fileName) {
161 18
            @unlink($fileName);
162
        }
163 460
        self::$_filesToCleanUp = [];
164
165 460
        if (is_null(self::$_container)) {
166
            return;
167
        }
168
169 460
        self::$_container->mockery_teardown();
170 458
        self::$_container->mockery_close();
171 458
        self::$_container = null;
172 458
    }
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 498
    public static function getContainer()
193
    {
194 498
        if (is_null(self::$_container)) {
195 453
            self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
196
        }
197
198 498
        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 453
    public static function getGenerator()
218
    {
219 453
        if (is_null(self::$_generator)) {
220 1
            self::$_generator = self::getDefaultGenerator();
221
        }
222
223 453
        return self::$_generator;
224
    }
225
226
    /**
227
     * Creates and returns a default generator
228
     * used inside this class.
229
     *
230
     * @return CachingGenerator
231
     */
232 430
    public static function getDefaultGenerator()
233
    {
234 430
        return new CachingGenerator(StringManipulationGenerator::withDefaultPasses());
235
    }
236
237
    /**
238
     * Setter for the $_loader static property.
239
     *
240
     * @param Loader $loader
241
     */
242
    public static function setLoader(Loader $loader)
243
    {
244
        self::$_loader = $loader;
245
    }
246
247
    /**
248
     * Lazy loader method and getter for
249
     * the $_loader property.
250
     *
251
     * @return Loader
252
     */
253 453
    public static function getLoader()
254
    {
255 453
        if (is_null(self::$_loader)) {
256 1
            self::$_loader = self::getDefaultLoader();
257
        }
258
259 453
        return self::$_loader;
260
    }
261
262
    /**
263
     * Gets an EvalLoader to be used as default.
264
     *
265
     * @return EvalLoader
266
     */
267 307
    public static function getDefaultLoader()
268
    {
269 307
        return new EvalLoader();
270
    }
271
272
    /**
273
     * Set the container.
274
     *
275
     * @param \Mockery\Container $container
276
     *
277
     * @return \Mockery\Container
278
     */
279 18
    public static function setContainer(Mockery\Container $container)
280
    {
281 18
        return self::$_container = $container;
282
    }
283
284
    /**
285
     * Reset the container to null.
286
     *
287
     * @return void
288
     */
289 14
    public static function resetContainer()
290
    {
291 14
        self::$_container = null;
292 14
    }
293
294
    /**
295
     * Return instance of ANY matcher.
296
     *
297
     * @return \Mockery\Matcher\Any
298
     */
299 5
    public static function any()
300
    {
301 5
        return new \Mockery\Matcher\Any();
302
    }
303
304
    /**
305
     * Return instance of TYPE matcher.
306
     *
307
     * @param $expected
308
     *
309
     * @return \Mockery\Matcher\Type
310
     */
311 47
    public static function type($expected)
312
    {
313 47
        return new \Mockery\Matcher\Type($expected);
314
    }
315
316
    /**
317
     * Return instance of DUCKTYPE matcher.
318
     *
319
     * @param array $args
320
     *
321
     * @return \Mockery\Matcher\Ducktype
322
     */
323 3
    public static function ducktype(...$args)
324
    {
325 3
        return new \Mockery\Matcher\Ducktype($args);
326
    }
327
328
    /**
329
     * Return instance of SUBSET matcher.
330
     *
331
     * @param array $part
332
     * @param bool $strict - (Optional) True for strict comparison, false for loose
333
     *
334
     * @return \Mockery\Matcher\Subset
335
     */
336 3
    public static function subset(array $part, $strict = true)
337
    {
338 3
        return new \Mockery\Matcher\Subset($part, $strict);
339
    }
340
341
    /**
342
     * Return instance of CONTAINS matcher.
343
     *
344
     * @param array $args
345
     *
346
     * @return \Mockery\Matcher\Contains
347
     */
348 3
    public static function contains(...$args)
349
    {
350 3
        return new \Mockery\Matcher\Contains($args);
351
    }
352
353
    /**
354
     * Return instance of HASKEY matcher.
355
     *
356
     * @param $key
357
     *
358
     * @return \Mockery\Matcher\HasKey
359
     */
360 4
    public static function hasKey($key)
361
    {
362 4
        return new \Mockery\Matcher\HasKey($key);
363
    }
364
365
    /**
366
     * Return instance of HASVALUE matcher.
367
     *
368
     * @param $val
369
     *
370
     * @return \Mockery\Matcher\HasValue
371
     */
372 3
    public static function hasValue($val)
373
    {
374 3
        return new \Mockery\Matcher\HasValue($val);
375
    }
376
377
    /**
378
     * Return instance of CLOSURE matcher.
379
     *
380
     * @param $closure
381
     *
382
     * @return \Mockery\Matcher\Closure
383
     */
384 6
    public static function on($closure)
385
    {
386 6
        return new \Mockery\Matcher\Closure($closure);
387
    }
388
389
    /**
390
     * Return instance of MUSTBE matcher.
391
     *
392
     * @param $expected
393
     *
394
     * @return \Mockery\Matcher\MustBe
395
     */
396 6
    public static function mustBe($expected)
397
    {
398 6
        return new \Mockery\Matcher\MustBe($expected);
0 ignored issues
show
Deprecated Code introduced by
The class Mockery\Matcher\MustBe has been deprecated with message: 2.0 Due to ambiguity, use Hamcrest or PHPUnit equivalents

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

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

Loading history...
399
    }
400
401
    /**
402
     * Return instance of NOT matcher.
403
     *
404
     * @param $expected
405
     *
406
     * @return \Mockery\Matcher\Not
407
     */
408 3
    public static function not($expected)
409
    {
410 3
        return new \Mockery\Matcher\Not($expected);
411
    }
412
413
    /**
414
     * Return instance of ANYOF matcher.
415
     *
416
     * @param array $args
417
     *
418
     * @return \Mockery\Matcher\AnyOf
419
     */
420 3
    public static function anyOf(...$args)
421
    {
422 3
        return new \Mockery\Matcher\AnyOf($args);
423
    }
424
425
    /**
426
     * Return instance of NOTANYOF matcher.
427
     *
428
     * @param array $args
429
     *
430
     * @return \Mockery\Matcher\NotAnyOf
431
     */
432 3
    public static function notAnyOf(...$args)
433
    {
434 3
        return new \Mockery\Matcher\NotAnyOf($args);
435
    }
436
437
    /**
438
     * Lazy loader and Getter for the global
439
     * configuration container.
440
     *
441
     * @return \Mockery\Configuration
442
     */
443 486
    public static function getConfiguration()
444
    {
445 486
        if (is_null(self::$_config)) {
446 1
            self::$_config = new \Mockery\Configuration();
447
        }
448
449 486
        return self::$_config;
450
    }
451
452
    /**
453
     * Utility method to format method name and arguments into a string.
454
     *
455
     * @param string $method
456
     * @param array $arguments
457
     *
458
     * @return string
459
     */
460 102
    public static function formatArgs($method, array $arguments = null)
461
    {
462 102
        if (is_null($arguments)) {
463
            return $method . '()';
464
        }
465
466 102
        $formattedArguments = array();
467 102
        foreach ($arguments as $argument) {
468 101
            $formattedArguments[] = self::formatArgument($argument);
469
        }
470
471 102
        return $method . '(' . implode(', ', $formattedArguments) . ')';
472
    }
473
474
    /**
475
     * Gets the string representation
476
     * of any passed argument.
477
     *
478
     * @param $argument
479
     * @param $depth
480
     *
481
     * @return string
482
     */
483 101
    private static function formatArgument($argument, $depth = 0)
484
    {
485 101
        if ($argument instanceOf MatcherAbstract) {
486 41
            return (string) $argument;
487
        }
488
489 60
        if (is_object($argument)) {
490 7
            return 'object(' . get_class($argument) . ')';
491
        }
492
493 57
        if (is_int($argument) || is_float($argument)) {
494 40
            return $argument;
495
        }
496
497 26
        if (is_array($argument)) {
498 13
            if ($depth === 1) {
499 2
                $argument = '[...]';
500
            } 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
                }
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
        }
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
        );
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
        }
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 1
            } catch (\Exception $e) {
637 1
                $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
638
            }
639
        }
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
            } elseif (is_object($value)) {
689
                $argument[$key] = self::objectToArray($value, $nesting - 1);
690
            }
691
        }
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 342
    public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
706
    {
707 342
        $composite = new \Mockery\CompositeExpectation();
708
709 342
        foreach ($args as $arg) {
710 342
            if (is_array($arg)) {
711 18
                foreach ($arg as $k => $v) {
712 18
                    $expectation = self::buildDemeterChain($mock, $k, $add)->andReturn($v);
0 ignored issues
show
Unused Code introduced by
The call to ExpectationInterface::andReturn() has too many arguments starting with $v.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
713 18
                    $composite->add($expectation);
0 ignored issues
show
Documentation introduced by
$expectation is of type object<Mockery\ExpectationInterface>, but the function expects a object<Mockery\Expectati...y\CompositeExpectation>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
714
                }
715
            } elseif (is_string($arg)) {
716 328
                $expectation = self::buildDemeterChain($mock, $arg, $add);
717 322
                $composite->add($expectation);
0 ignored issues
show
Documentation introduced by
$expectation is of type object<Mockery\ExpectationInterface>, but the function expects a object<Mockery\Expectati...y\CompositeExpectation>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
718
            }
719
        }
720
721 336
        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 342
    protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
735
    {
736
        /** @var Mockery\Container $container */
737 342
        $container = $mock->mockery_getContainer();
738 342
        $methodNames = explode('->', $arg);
739 342
        reset($methodNames);
740
741 342
        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
742 7
            && !$mock->mockery_isAnonymous()
743 6
            && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
744
        ) {
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
            );
750
        }
751
752
        /** @var ExpectationInterface|null $expectations */
753 338
        $expectations = null;
754
755
        /** @var Callable $nextExp */
756
        $nextExp = function ($method) use ($add) {
757 338
            return $add($method);
758 338
        };
759
760 338
        while (true) {
761 338
            $method = array_shift($methodNames);
762 338
            $expectations = $mock->mockery_getExpectationsFor($method);
763
764 338
            if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
765 338
                $expectations = $nextExp($method);
766 336
                if (self::noMoreElementsInChain($methodNames)) {
767 336
                    break;
768
                }
769
770 11
                $mock = self::getNewDemeterMock($container, $method, $expectations);
771
            } else {
772 5
                $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
773 5
                if ($demeterMockKey) {
774 5
                    $mock = self::getExistingDemeterMock($container, $demeterMockKey);
775
                }
776
            }
777
778 11
            $nextExp = function ($n) use ($mock) {
779 11
                return $mock->shouldReceive($n);
780 11
            };
781
        }
782
783 336
        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 336
    private static function noMoreElementsInChain(array $methodNames)
835
    {
836 336
        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
        }
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