Passed
Push — master ( 62af2e...d65fca )
by Théo
07:27 queued 04:04
created

StringScalarPrefixer::enterNode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\PhpParser\NodeVisitor;
16
17
use Humbug\PhpScoper\Reflector;
18
use PhpParser\Node;
19
use PhpParser\Node\Arg;
20
use PhpParser\Node\Const_;
21
use PhpParser\Node\Expr\ArrayItem;
22
use PhpParser\Node\Expr\Assign;
23
use PhpParser\Node\Expr\FuncCall;
24
use PhpParser\Node\Name;
25
use PhpParser\Node\Name\FullyQualified;
26
use PhpParser\Node\Param;
27
use PhpParser\Node\Scalar\String_;
28
use PhpParser\Node\Stmt\PropertyProperty;
29
use PhpParser\NodeVisitorAbstract;
30
use function is_string;
31
use function preg_match;
32
33
/**
34
 * Prefixes the string scalar values.
35
 *
36
 * ```
37
 * $x = 'Foo\Bar';
38
 * ```
39
 *
40
 * =>
41
 *
42
 * ```
43
 * $x = 'Humbug\Foo\Bar';
44
 * ```
45
 *
46
 * @private
47
 */
48
final class StringScalarPrefixer extends NodeVisitorAbstract
49
{
50
    private $prefix;
51
    private $reflector;
52
53 371
    public function __construct(string $prefix, Reflector $reflector)
54
    {
55 371
        $this->prefix = $prefix;
56 371
        $this->reflector = $reflector;
57
    }
58
59
    /**
60
     * @inheritdoc
61
     */
62 370
    public function enterNode(Node $node): Node
63
    {
64 370
        return ($this->shouldPrefixScalar($node))
65 11
            ? $this->prefixStringScalar($node)
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Scalar\String_>. 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...
66 370
            : $node
67
        ;
68
    }
69
70 370
    private function shouldPrefixScalar(Node $node): bool
71
    {
72 370
        if (false === ($node instanceof String_ && AppendParentNode::hasParent($node) && is_string($node->value))
73 370
            || 1 !== preg_match('/^\\\\*(?:\p{L}+\\\\+)++\p{L}+$/u', $node->value)
74
        ) {
75 370
            return false;
76
        }
77
        /** @var String_ $node */
78 13
        $parentNode = AppendParentNode::getParent($node);
79
80 13
        if ($parentNode instanceof Arg
81 13
            && null !== $funcNode = AppendParentNode::findParent($parentNode)
82
        ) {
83 2
            $funcNode = AppendParentNode::getParent($parentNode);
84
85
            return
86 2
                $funcNode instanceof FuncCall
87 2
                && $funcNode->name instanceof Name
88 2
                && false === $funcNode->hasAttribute('whitelist_class_alias')
89
            ;
90
        }
91
92 11
        return $parentNode instanceof Assign
93 11
            || $parentNode instanceof ArrayItem
94 11
            || $parentNode instanceof Param
95 11
            || $parentNode instanceof Const_
96 11
            || $parentNode instanceof PropertyProperty
97
        ;
98
    }
99
100 11
    private function prefixStringScalar(String_ $string): Node
101
    {
102 11
        $stringName = new Name(
103 11
            preg_replace('/^\\\\(.+)$/', '$1', $string->value),
104 11
            $string->getAttributes()
105
        );
106
107
        // Skip if is already prefixed
108 11
        if ($this->prefix === $stringName->getFirst()) {
109 10
            $newStringName = $stringName;
110
        // Check if the class can be prefixed: class from the global namespace
111
        } elseif (
112 11
            1 === count($stringName->parts)
113 11
            || $this->reflector->isClassInternal($stringName->toString())
114
        ) {
115
            $newStringName = $stringName;
116
        } else {
117 11
            $newStringName = FullyQualified::concat($this->prefix, $stringName->toString(), $stringName->getAttributes());
118
        }
119
120 11
        return new String_($newStringName->toString(), $string->getAttributes());
121
    }
122
}
123