Completed
Pull Request — master (#264)
by Théo
02:53
created

MagicCallPatch   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 7
Bugs 1 Features 2
Metric Value
wmc 15
c 7
b 1
f 2
lcom 1
cbo 5
dl 0
loc 110
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 3
A supports() 0 4 1
B apply() 0 26 4
A getPriority() 0 4 1
A getClassInterfacesTagList() 0 11 2
A getClassTagList() 0 13 4
1
<?php
2
3
/*
4
 * This file is part of the Prophecy.
5
 * (c) Konstantin Kudryashov <[email protected]>
6
 *     Marcello Duarte <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Prophecy\Doubler\ClassPatch;
13
14
use phpDocumentor\Reflection\DocBlock;
15
use phpDocumentor\Reflection\DocBlock\Tag;
16
use phpDocumentor\Reflection\DocBlock\Tag as LegacyTag;
17
use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
18
use phpDocumentor\Reflection\DocBlock\Tags\Method;
19
use phpDocumentor\Reflection\DocBlockFactory;
20
use phpDocumentor\Reflection\DocBlockFactoryInterface;
21
use phpDocumentor\Reflection\Types\ContextFactory;
22
use Prophecy\Doubler\Generator\Node\ClassNode;
23
use Prophecy\Doubler\Generator\Node\MethodNode;
24
25
/**
26
 * Discover Magical API using "@method" PHPDoc format.
27
 *
28
 * @author Thomas Tourlourat <[email protected]>
29
 * @author Kévin Dunglas <[email protected]>
30
 * @author Théo FIDRY <[email protected]>
31
 */
32
class MagicCallPatch implements ClassPatchInterface
33
{
34
    /**
35
     * @var DocBlockFactory|null
36
     */
37
    private $docBlockFactory;
38
39
    /**
40
     * @var ContextFactory|null
41
     */
42
    private $contextFactory;
43
44
    public function __construct()
45
    {
46
        if (class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory')) {
47
            $this->docBlockFactory = DocBlockFactory::createInstance();
48
            $this->contextFactory = new ContextFactory();
49
        }
50
    }
51
52
    /**
53
     * Support any class
54
     *
55
     * @param ClassNode $node
56
     *
57
     * @return boolean
58
     */
59
    public function supports(ClassNode $node)
60
    {
61
        return true;
62
    }
63
64
    /**
65
     * Discover Magical API
66
     *
67
     * @param ClassNode $node
68
     */
69
    public function apply(ClassNode $node)
70
    {
71
        $parentClass = $node->getParentClass();
72
        $reflectionClass = new \ReflectionClass($parentClass);
73
74
        $tagList = array_merge(
75
            $this->getClassTagList($reflectionClass),
76
            $this->getClassInterfacesTagList($reflectionClass)
77
        );
78
79
        foreach($tagList as $tag) {
80
            /* @var LegacyMethodTag|Method $tag */
81
            $methodName = $tag->getMethodName();
82
83
            if (empty($methodName)) {
84
                continue;
85
            }
86
87
            if (!$reflectionClass->hasMethod($methodName)) {
88
                $methodNode = new MethodNode($methodName);
89
                $methodNode->setStatic($tag->isStatic());
90
91
                $node->addMethod($methodNode);
92
            }
93
        }
94
    }
95
96
    /**
97
     * Returns patch priority, which determines when patch will be applied.
98
     *
99
     * @return integer Priority number (higher - earlier)
100
     */
101
    public function getPriority()
102
    {
103
        return 50;
104
    }
105
106
    /**
107
     * @param \ReflectionClass $reflectionClass
108
     *
109
     * @return LegacyTag[]
110
     */
111
    private function getClassInterfacesTagList(\ReflectionClass $reflectionClass)
112
    {
113
        $interfaces = $reflectionClass->getInterfaces();
114
        $tagList = array();
115
116
        foreach($interfaces as $interface) {
117
            $tagList = array_merge($tagList, $this->getClassTagList($interface));
118
        }
119
120
        return $tagList;
121
    }
122
123
    /**
124
     * @param \ReflectionClass $reflectionClass
125
     *
126
     * @return LegacyMethodTag[]|Method[]
127
     */
128
    private function getClassTagList(\ReflectionClass $reflectionClass)
129
    {
130
        try {
131
            $phpdoc = (null === $this->docBlockFactory || null === $this->contextFactory)
132
                ? new DocBlock($reflectionClass->getDocComment())
133
                : $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass))
134
            ;
135
136
            return $phpdoc->getTagsByName('method');
137
        } catch (\InvalidArgumentException $e) {
138
            return array();
139
        }
140
    }
141
}
142
143