Completed
Push — master ( 3e3691...a70c71 )
by Théo
02:26
created

StringScalarPrefixer   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 78
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 96.55%

Importance

Changes 0
Metric Value
dl 0
loc 78
ccs 28
cts 29
cp 0.9655
rs 10
c 0
b 0
f 0
wmc 13
lcom 1
cbo 7

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A enterNode() 0 7 2
B shouldPrefixScalar() 0 20 7
A prefixStringScalar() 0 20 3
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\Reflector;
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 $reflector;
45
46
    /**
47
     * @param string    $prefix
48
     * @param string[]  $whitelistedFunctions
49
     * @param string[]  $whitelist
50
     * @param Reflector $reflector
51
     */
52 342
    public function __construct(
53
        string $prefix,
54
        array $whitelistedFunctions,
55
        array $whitelist,
56
        Reflector $reflector
57
    ) {
58 342
        $this->prefix = $prefix;
59 342
        $this->whitelistedFunctions = $whitelistedFunctions;
60 342
        $this->whitelist = $whitelist;
61 342
        $this->reflector = $reflector;
62
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67 341
    public function enterNode(Node $node): Node
68
    {
69 341
        return ($this->shouldPrefixScalar($node))
70 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...
71 341
            : $node
72
        ;
73
    }
74
75 341
    private function shouldPrefixScalar(Node $node): bool
76
    {
77 341
        if (false === ($node instanceof String_ && AppendParentNode::hasParent($node))) {
78 341
            return false;
79
        }
80
        /** @var String_ $node */
81 78
        $parentNode = AppendParentNode::getParent($node);
82
83 78
        if (false === ($parentNode instanceof Arg) || false === AppendParentNode::hasParent($parentNode)) {
84 22
            return false;
85
        }
86
87 60
        $argParent = AppendParentNode::getParent($parentNode);
88
89
        return
90 60
            $argParent instanceof FuncCall
91 60
            && $argParent->name instanceof Name
92 60
            && in_array((string) $argParent->name, $this->whitelistedFunctions)
93
        ;
94
    }
95
96 2
    private function prefixStringScalar(String_ $string): Node
97
    {
98 2
        $stringName = new Name(
99 2
            preg_replace('/^\\\\(.+)$/', '$1', $string->value),
100 2
            $string->getAttributes()
101
        );
102
103
        // Skip if is already prefixed
104 2
        if ($this->prefix === $stringName->getFirst()) {
105 2
            $newStringName = $stringName;
106
        // Check if the class can be prefixed: class from the global namespace
107 2
        } elseif ($this->reflector->isClassInternal($stringName->toString())
108
        ) {
109
            $newStringName = $stringName;
110
        } else {
111 2
            $newStringName = FullyQualified::concat($this->prefix, $stringName->toString(), $stringName->getAttributes());
112
        }
113
114 2
        return new String_($newStringName->toString(), $string->getAttributes());
115
    }
116
}
117