Completed
Push — master ( fb8b80...a27aa2 )
by Théo
06:11 queued 04:17
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\NodeVisitor;
16
17
use Humbug\PhpScoper\NodeVisitor\Resolver\FullyQualifiedNameResolver;
18
use PhpParser\Node;
19
use PhpParser\Node\Arg;
20
use PhpParser\Node\Expr\FuncCall;
21
use PhpParser\Node\Name;
22
use PhpParser\Node\Name\FullyQualified;
23
use PhpParser\Node\Scalar\String_;
24
use PhpParser\NodeVisitorAbstract;
25
26
/**
27
 * Prefixes the string scalar values.
28
 *
29
 * ```
30
 * $x = 'Foo\Bar';
31
 * ```
32
 *
33
 * =>
34
 *
35
 * ```
36
 * $x = 'Humbug\Foo\Bar';
37
 * ```
38
 */
39
final class StringScalarPrefixer extends NodeVisitorAbstract
40
{
41
    private $prefix;
42
    private $whitelistedFunctions;
43
    private $whitelist;
44
    private $globalWhitelister;
45
    private $nameResolver;
46
47
    /**
48
     * @param string                     $prefix
49
     * @param string[]                   $whitelistedFunctions
50
     * @param string[]                   $whitelist
51
     * @param callable                   $globalWhitelister
52
     * @param FullyQualifiedNameResolver $nameResolver
53
     */
54 331
    public function __construct(
55
        string $prefix,
56
        array $whitelistedFunctions,
57
        array $whitelist,
58
        callable $globalWhitelister,
59
        FullyQualifiedNameResolver $nameResolver
60
    ) {
61 331
        $this->prefix = $prefix;
62 331
        $this->whitelistedFunctions = $whitelistedFunctions;
63 331
        $this->whitelist = $whitelist;
64 331
        $this->globalWhitelister = $globalWhitelister;
65 331
        $this->nameResolver = $nameResolver;
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71 330
    public function enterNode(Node $node): Node
72
    {
73 330
        return ($this->shouldPrefixScalar($node))
74 2
            ? $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...
75 330
            : $node
76
        ;
77
    }
78
79 330
    private function shouldPrefixScalar(Node $node): bool
80
    {
81 330
        if (false === ($node instanceof String_ && AppendParentNode::hasParent($node))) {
82 330
            return false;
83
        }
84
        /** @var String_ $node */
85 26
        $parentNode = AppendParentNode::getParent($node);
86
87 26
        if (false === ($parentNode instanceof Arg) || false === AppendParentNode::hasParent($parentNode)) {
88 12
            return false;
89
        }
90
91 18
        $argParent = AppendParentNode::getParent($parentNode);
92
93
        return
94 18
            $argParent instanceof FuncCall
95 12
            && $argParent->name instanceof Name
96 18
            && in_array((string) $argParent->name, $this->whitelistedFunctions)
97
        ;
98
    }
99
100 2
    private function prefixStringScalar(String_ $string): Node
101
    {
102 2
        $stringName = new Name(
103 2
            preg_replace('/^\\\\(.+)$/', '$1', $string->value),
104 2
            $string->getAttributes()
105
        );
106
107
        // Skip if is already prefixed
108 2
        if ($this->prefix === $stringName->getFirst()) {
109 2
            $newStringName = $stringName;
110
            // Check if the class can be prefixed: class from the global namespace
111 2
        } elseif (1 === count($stringName->parts)
112
            && false === ($this->globalWhitelister)($stringName->toString())
113
        ) {
114
            $newStringName = $stringName;
115
            // Check if the class can be prefixed: regular class
116 2
        } elseif (1 < count($stringName->parts)
117 2
            && in_array($stringName->toString(), $this->whitelist)
118
        ) {
119 1
            $newStringName = $stringName;
120
        } else {
121 1
            $newStringName = FullyQualified::concat($this->prefix, $stringName->toString(), $stringName->getAttributes());
122
        }
123
124 2
        return new String_($newStringName->toString(), $string->getAttributes());
125
    }
126
}
127