Passed
Push — master ( 902a34...c826a7 )
by Kyle
53s queued 11s
created

src/main/php/PHPMD/AbstractNode.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is part of PHP Mess Detector.
4
 *
5
 * Copyright (c) Manuel Pichler <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * Licensed under BSD License
9
 * For full copyright and license information, please see the LICENSE file.
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @author Manuel Pichler <[email protected]>
13
 * @copyright Manuel Pichler. All rights reserved.
14
 * @license https://opensource.org/licenses/bsd-license.php BSD License
15
 * @link http://phpmd.org/
16
 */
17
18
namespace PHPMD;
19
20
use PDepend\Source\AST\AbstractASTArtifact;
21
use PHPMD\Node\ASTNode;
22
23
/**
24
 * This is an abstract base class for PHPMD code nodes, it is just a wrapper
25
 * around PDepend's object model.
26
 */
27
abstract class AbstractNode
28
{
29
    /**
30
     * @var \PDepend\Source\AST\ASTArtifact|\PDepend\Source\AST\ASTNode $node
31
     */
32
    private $node = null;
33
34
    /**
35
     * The collected metrics for this node.
36
     *
37
     * @var array<string, mixed>
38
     */
39
    private $metrics = null;
40
41
    /**
42
     * Constructs a new PHPMD node.
43
     *
44
     * @param \PDepend\Source\AST\ASTArtifact|\PDepend\Source\AST\ASTNode $node
45 59
     */
46
    public function __construct($node)
47 59
    {
48 59
        $this->node = $node;
49
    }
50
51
    /**
52
     * The magic call method is used to pipe requests from rules direct
53
     * to the underlying PDepend AST node.
54
     *
55
     * @param string $name
56
     * @param array $args
57
     * @return mixed
58
     * @throws \BadMethodCallException When the underlying PDepend node
59
     *         does not contain a method named <b>$name</b>.
60 36
     */
61
    public function __call($name, array $args)
62 36
    {
63 36
        $node = $this->getNode();
64
        if (!method_exists($node, $name)) {
65
            throw new \BadMethodCallException(
66
                sprintf('Invalid method %s() called.', $name)
67
            );
68 36
        }
69
70
        return call_user_func_array(array($node, $name), $args);
71
    }
72
73
    /**
74
     * Returns the parent of this node or <b>null</b> when no parent node
75
     * exists.
76
     *
77 8
     * @return ASTNode
78
     */
79 8
    public function getParent()
80
    {
81
        $node = $this->node->getParent();
0 ignored issues
show
The method getParent does only exist in PDepend\Source\AST\ASTNode, but not in PDepend\Source\AST\ASTArtifact.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
82 8
        if ($node === null) {
83
            return null;
84
        }
85
86
        return new ASTNode($node, $this->getFileName());
87
    }
88
89
    /**
90
     * Returns a child node at the given index.
91 13
     *
92
     * @param integer $index The child offset.
93 13
     * @return \PHPMD\Node\ASTNode
94 13
     */
95 13
    public function getChild($index)
96
    {
97
        return new ASTNode(
98
            $this->node->getChild($index),
99
            $this->getFileName()
100
        );
101
    }
102
103
    /**
104
     * Returns the first child of the given type or <b>null</b> when this node
105
     * has no child of the given type.
106 22
     *
107
     * @param string $type The searched child type.
108 22
     * @return ASTNode|null
109 22
     */
110
    public function getFirstChildOfType($type)
111
    {
112 22
        $node = $this->node->getFirstChildOfType('PDepend\Source\AST\AST' . $type);
113
114
        if ($node === null) {
115
            return null;
116
        }
117
118
        return new ASTNode($node, $this->getFileName());
119
    }
120
121
    /**
122 55
     * Searches recursive for all children of this node that are of the given
123
     * type.
124 55
     *
125
     * @param string $type The searched child type.
126 55
     * @return ASTNode[]
127 55
     */
128 51
    public function findChildrenOfType($type)
129
    {
130 55
        $children = $this->node->findChildrenOfType('PDepend\Source\AST\AST' . $type);
131
132
        $nodes = array();
133
134
        foreach ($children as $child) {
135
            $nodes[] = new ASTNode($child, $this->getFileName());
136
        }
137
138
        return $nodes;
139 2
    }
140
141 2
    /**
142 2
     * Tests if this node represents the the given type.
143
     *
144
     * @param string $type The expected node type.
145
     * @return boolean
146
     */
147
    public function isInstanceOf($type)
148
    {
149
        $class = 'PDepend\Source\AST\AST' . $type;
150 2
151
        return ($this->node instanceof $class);
152 2
    }
153
154
    /**
155
     * Returns the image of the underlying node.
156
     *
157
     * @return string
158
     */
159
    public function getImage()
160
    {
161 15
        return $this->node->getName();
162
    }
163 15
164
    /**
165
     * Returns the source name for this node, maybe a class or interface name,
166
     * or a package, method, function name.
167
     *
168
     * @return string
169
     */
170
    public function getName()
171 6
    {
172
        return $this->node->getName();
173 6
    }
174
175
    /**
176
     * Returns the begin line for this node in the php source code file.
177
     *
178
     * @return integer
179
     */
180
    public function getBeginLine()
181
    {
182
        return $this->node->getStartLine();
183
    }
184
185
    /**
186
     * Returns the end line for this node in the php source code file.
187
     *
188
     * @return integer
189
     */
190
    public function getEndLine()
191 54
    {
192
        return $this->node->getEndLine();
193 54
    }
194
195
    /**
196
     * Returns the name of the declaring source file.
197
     *
198
     * @return string|null
199
     */
200
    public function getFileName()
201 51
    {
202
        $compilationUnit = $this->node instanceof AbstractASTArtifact
203 51
            ? $this->node->getCompilationUnit()
204
            : null;
205
206
        return $compilationUnit
207
            ? (string)$compilationUnit->getFileName()
208
            : null; // @TODO: Find the name from some parent node https://github.com/phpmd/phpmd/issues/837
209
    }
210
211 3
    /**
212
     * Returns the wrapped PDepend node instance.
213 3
     *
214 3
     * @return \PDepend\Source\AST\ASTArtifact
215
     */
216
    public function getNode()
217
    {
218
        return $this->node;
219
    }
220
221
    /**
222
     * Returns a textual representation/name for the concrete node type.
223
     *
224 3
     * @return string
225
     */
226 3
    public function getType()
227 3
    {
228
        $type = explode('\\', get_class($this));
229
230
        return preg_replace('(node$)', '', strtolower(array_pop($type)));
231
    }
232
233
    /**
234
     * This method will return the metric value for the given identifier or
235
     * <b>null</b> when no such metric exists.
236
     *
237
     * @param string $name The metric name or abbreviation.
238 6
     * @return mixed
239
     */
240 6
    public function getMetric($name)
241 6
    {
242
        if (isset($this->metrics[$name])) {
243 6
            return $this->metrics[$name];
244
        }
245
246
        return null;
247
    }
248
249
    /**
250
     * This method will set the metrics for this node.
251
     *
252
     * @param array<string, mixed> $metrics The collected node metrics.
253
     * @return void
254
     */
255
    public function setMetrics(array $metrics)
256
    {
257
        if ($this->metrics === null) {
258
            $this->metrics = $metrics;
259
        }
260
    }
261
262
    /**
263
     * Checks if this node has a suppressed annotation for the given rule
264
     * instance.
265
     *
266
     * @param \PHPMD\Rule $rule
267
     * @return boolean
268
     */
269
    abstract public function hasSuppressWarningsAnnotationFor(Rule $rule);
270
271
    /**
272
     * Returns the full qualified name of a class, an interface, a method or
273
     * a function.
274
     *
275
     * @return string
276
     */
277
    abstract public function getFullQualifiedName();
278
279
    /**
280
     * Returns the name of the parent type or <b>null</b> when this node has no
281
     * parent type.
282
     *
283
     * @return string
284
     */
285
    abstract public function getParentName();
286
287
    /**
288
     * Returns the name of the parent package.
289
     *
290
     * @return string
291
     */
292
    abstract public function getNamespaceName();
293
}
294