Completed
Pull Request — master (#97)
by Théo
03:28 queued 01:25
created

StringScalarPrefixer::shouldPrefixScalar()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 4
nop 1
dl 0
loc 19
rs 8.8571
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
    public function __construct(
55
        string $prefix,
56
        array $whitelistedFunctions,
57
        array $whitelist,
58
        callable $globalWhitelister,
59
        FullyQualifiedNameResolver $nameResolver
60
    ) {
61
        $this->prefix = $prefix;
62
        $this->whitelistedFunctions = $whitelistedFunctions;
63
        $this->whitelist = $whitelist;
64
        $this->globalWhitelister = $globalWhitelister;
65
        $this->nameResolver = $nameResolver;
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71
    public function enterNode(Node $node): Node
72
    {
73
        return ($this->shouldPrefixScalar($node))
74
            ? $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
            : $node
76
        ;
77
    }
78
79
    private function shouldPrefixScalar(Node $node): bool
80
    {
81
        if (false === ($node instanceof String_ && AppendParentNode::hasParent($node))) {
82
            return false;
83
        }
84
        /** @var String_ $node */
85
        $parentNode = AppendParentNode::getParent($node);
86
87
        if (false === ($parentNode instanceof Arg) || false === AppendParentNode::hasParent($parentNode)) {
88
            return false;
89
        }
90
91
        $argParent = AppendParentNode::getParent($parentNode);
92
93
        return
94
            $argParent instanceof FuncCall
95
            && in_array((string) $argParent->name, $this->whitelistedFunctions)
96
        ;
97
    }
98
99
    private function prefixStringScalar(String_ $string): Node
100
    {
101
        $stringName = new Name(
102
            preg_replace('/^\\\\(.+)$/', '$1', $string->value),
103
            $string->getAttributes()
104
        );
105
106
        // Skip if is already prefixed
107
        if ($this->prefix === $stringName->getFirst()) {
108
            $newStringName = $stringName;
109
            // Check if the class can be prefixed: class from the global namespace
110
        } elseif (1 === count($stringName->parts)
111
            && false === ($this->globalWhitelister)($stringName->toString())
112
        ) {
113
            $newStringName = $stringName;
114
            // Check if the class can be prefixed: regular class
115
        } elseif (1 < count($stringName->parts)
116
            && in_array($stringName->toString(), $this->whitelist)
117
        ) {
118
            $newStringName = $stringName;
119
        } else {
120
            $newStringName = FullyQualified::concat($this->prefix, $stringName->toString(), $stringName->getAttributes());
121
        }
122
123
        return new String_($newStringName->toString(), $string->getAttributes());
124
    }
125
}
126