Completed
Push — master ( 454e04...38876e )
by Marco
21s queued 11s
created

ReflectionConstant::getStartLine()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BetterReflection\Reflection;
6
7
use Exception;
8
use PhpParser\Node;
9
use PhpParser\Node\Stmt\Namespace_ as NamespaceNode;
10
use Reflector as CoreReflector;
11
use Roave\BetterReflection\BetterReflection;
12
use Roave\BetterReflection\NodeCompiler\CompileNodeToValue;
13
use Roave\BetterReflection\NodeCompiler\CompilerContext;
14
use Roave\BetterReflection\Reflection\Exception\InvalidConstantNode;
15
use Roave\BetterReflection\Reflection\StringCast\ReflectionConstantStringCast;
16
use Roave\BetterReflection\Reflector\Exception\IdentifierNotFound;
17
use Roave\BetterReflection\Reflector\Reflector;
18
use Roave\BetterReflection\SourceLocator\Located\LocatedSource;
19
use Roave\BetterReflection\Util\CalculateReflectionColum;
20
use Roave\BetterReflection\Util\ConstantNodeChecker;
21
use Roave\BetterReflection\Util\GetFirstDocComment;
22
use function array_slice;
23
use function count;
24
use function explode;
25
use function implode;
26
use function substr_count;
27
28
class ReflectionConstant implements Reflection, CoreReflector
29
{
30
    /** @var Reflector */
31
    private $reflector;
32
33
    /** @var Node\Stmt\Const_|Node\Expr\FuncCall */
34
    private $node;
35
36
    /** @var LocatedSource */
37
    private $locatedSource;
38
39
    /** @var NamespaceNode|null */
40
    private $declaringNamespace;
41
42
    /** @var int|null */
43
    private $positionInNode;
44
45
    /** @var int|float|mixed[]|string|bool|null const value */
46
    private $value;
47
48
    /** @var bool */
49
    private $valueWasCached = false;
50
51
    private function __construct()
52
    {
53
    }
54
55
    /**
56
     * Create a ReflectionConstant by name, using default reflectors etc.
57
     *
58
     * @throws IdentifierNotFound
59
     */
60
    public static function createFromName(string $constantName) : self
61
    {
62
        return (new BetterReflection())->constantReflector()->reflect($constantName);
63
    }
64
65
    /**
66
     * Create a reflection of a constant
67
     *
68
     * @internal
69
     *
70
     * @param Node\Stmt\Const_|Node\Expr\FuncCall $node Node has to be processed by the PhpParser\NodeVisitor\NameResolver
71
     */
72
    public static function createFromNode(
73
        Reflector $reflector,
74
        Node $node,
75
        LocatedSource $locatedSource,
76
        ?NamespaceNode $namespace = null,
77
        ?int $positionInNode = null
78
    ) : self {
79
        return $node instanceof Node\Stmt\Const_
80
            ? self::createFromConstKeyword($reflector, $node, $locatedSource, $namespace, $positionInNode)
81
            : self::createFromDefineFunctionCall($reflector, $node, $locatedSource);
0 ignored issues
show
Compatibility introduced by Jaroslav Hanslík
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Expr\FuncCall>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
82
    }
83
84
    private static function createFromConstKeyword(
85
        Reflector $reflector,
86
        Node\Stmt\Const_ $node,
87
        LocatedSource $locatedSource,
88
        ?NamespaceNode $namespace,
89
        int $positionInNode
90
    ) : self {
91
        $constant                     = new self();
92
        $constant->reflector          = $reflector;
93
        $constant->node               = $node;
94
        $constant->locatedSource      = $locatedSource;
95
        $constant->declaringNamespace = $namespace;
96
        $constant->positionInNode     = $positionInNode;
97
98
        return $constant;
99
    }
100
101
    /**
102
     * @throws InvalidConstantNode
103
     */
104
    private static function createFromDefineFunctionCall(
105
        Reflector $reflector,
106
        Node\Expr\FuncCall $node,
107
        LocatedSource $locatedSource
108
    ) : self {
109
        ConstantNodeChecker::assertValidDefineFunctionCall($node);
110
111
        $constant                = new self();
112
        $constant->reflector     = $reflector;
113
        $constant->node          = $node;
114
        $constant->locatedSource = $locatedSource;
115
116
        return $constant;
117
    }
118
119
    /**
120
     * Get the "short" name of the constant (e.g. for A\B\FOO, this will return
121
     * "FOO").
122
     */
123
    public function getShortName() : string
124
    {
125
        if ($this->node instanceof Node\Expr\FuncCall) {
126
            $nameParts = explode('\\', $this->getNameFromDefineFunctionCall($this->node));
127
128
            return $nameParts[count($nameParts) - 1];
129
        }
130
131
        /** @psalm-suppress PossiblyNullArrayOffset */
132
        return $this->node->consts[$this->positionInNode]->name->name;
133
    }
134
135
    /**
136
     * Get the "full" name of the constant (e.g. for A\B\FOO, this will return
137
     * "A\B\FOO").
138
     */
139
    public function getName() : string
140
    {
141
        if (! $this->inNamespace()) {
142
            return $this->getShortName();
143
        }
144
145
        if ($this->node instanceof Node\Expr\FuncCall) {
146
            return $this->getNameFromDefineFunctionCall($this->node);
147
        }
148
149
        /**
150
         * @psalm-suppress UndefinedPropertyFetch
151
         * @psalm-suppress PossiblyNullArrayOffset
152
         */
153
        return $this->node->consts[$this->positionInNode]->namespacedName->toString();
154
    }
155
156
    /**
157
     * Get the "namespace" name of the constant (e.g. for A\B\FOO, this will
158
     * return "A\B").
159
     *
160
     * @psalm-suppress PossiblyNullPropertyFetch
161
     */
162
    public function getNamespaceName() : string
163
    {
164
        if (! $this->inNamespace()) {
165
            return '';
166
        }
167
168
        $namespaceParts = $this->node instanceof Node\Expr\FuncCall
169
            ? array_slice(explode('\\', $this->getNameFromDefineFunctionCall($this->node)), 0, -1)
170
            : $this->declaringNamespace->name->parts;
171
172
        return implode('\\', $namespaceParts);
173
    }
174
175
    /**
176
     * Decide if this constant is part of a namespace. Returns false if the constant
177
     * is in the global namespace or does not have a specified namespace.
178
     */
179
    public function inNamespace() : bool
180
    {
181
        if ($this->node instanceof Node\Expr\FuncCall) {
182
            return substr_count($this->getNameFromDefineFunctionCall($this->node), '\\') !== 0;
183
        }
184
185
        return $this->declaringNamespace !== null
186
            && $this->declaringNamespace->name !== null;
187
    }
188
189
    public function getExtensionName() : ?string
190
    {
191
        return $this->locatedSource->getExtensionName();
192
    }
193
194
    /**
195
     * Is this an internal constant?
196
     */
197
    public function isInternal() : bool
198
    {
199
        return $this->locatedSource->isInternal();
200
    }
201
202
    /**
203
     * Is this a user-defined function (will always return the opposite of
204
     * whatever isInternal returns).
205
     */
206
    public function isUserDefined() : bool
207
    {
208
        return ! $this->isInternal();
209
    }
210
211
    /**
212
     * Returns constant value
213
     *
214
     * @return mixed
215
     */
216
    public function getValue()
217
    {
218
        if ($this->valueWasCached !== false) {
219
            return $this->value;
220
        }
221
222
        /** @psalm-suppress PossiblyNullArrayOffset */
223
        $valueNode = $this->node instanceof Node\Expr\FuncCall
224
            ? $this->node->args[1]->value
225
            : $this->node->consts[$this->positionInNode]->value;
226
227
        /** @psalm-suppress UndefinedPropertyFetch */
228
        $this->value          = (new CompileNodeToValue())->__invoke(
229
            $valueNode,
230
            new CompilerContext($this->reflector, null)
231
        );
232
        $this->valueWasCached = true;
233
234
        return $this->value;
235
    }
236
237
    public function getFileName() : ?string
238
    {
239
        return $this->locatedSource->getFileName();
240
    }
241
242
    public function getLocatedSource() : LocatedSource
243
    {
244
        return $this->locatedSource;
245
    }
246
247
    /**
248
     * Get the line number that this constant starts on.
249
     */
250
    public function getStartLine() : int
251
    {
252
        return $this->node->getStartLine();
253
    }
254
255
    /**
256
     * Get the line number that this constant ends on.
257
     */
258
    public function getEndLine() : int
259
    {
260
        return $this->node->getEndLine();
261
    }
262
263
    public function getStartColumn() : int
264
    {
265
        return CalculateReflectionColum::getStartColumn($this->locatedSource->getSource(), $this->node);
266
    }
267
268
    public function getEndColumn() : int
269
    {
270
        return CalculateReflectionColum::getEndColumn($this->locatedSource->getSource(), $this->node);
271
    }
272
273
    /**
274
     * Returns the doc comment for this constant
275
     */
276
    public function getDocComment() : string
277
    {
278
        return GetFirstDocComment::forNode($this->node);
279
    }
280
281
    /**
282
     * {@inheritDoc}
283
     */
284
    public function __toString() : string
285
    {
286
        return ReflectionConstantStringCast::toString($this);
287
    }
288
289
    public static function export() : void
290
    {
291
        throw new Exception('Unable to export statically');
292
    }
293
294
    /**
295
     * @return Node\Stmt\Const_|Node\Expr\FuncCall
296
     */
297
    public function getAst() : Node
298
    {
299
        return $this->node;
300
    }
301
302
    private function getNameFromDefineFunctionCall(Node\Expr\FuncCall $node) : string
303
    {
304
        /** @var Node\Scalar\String_ $nameNode */
305
        $nameNode = $node->args[0]->value;
306
307
        return $nameNode->value;
308
    }
309
}
310