FileEditor::removeBackslashes()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 7
rs 9.4286
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
/**
3
 * Author: Nil Portugués Calderó <[email protected]>
4
 * Date: 11/1/15
5
 * Time: 12:30 AM
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace NilPortugues\BackslashFixer\Fixer;
11
12
use NilPortugues\BackslashFixer\Fixer\Interfaces\FileSystem as FileSystemInterface;
13
use Zend\Code\Generator\FileGenerator;
14
15
class FileEditor
16
{
17
    /**
18
     * @var array
19
     */
20
    private static $constants = [];
21
    /**
22
     * @var \Zend\Code\Generator\FileGenerator
23
     */
24
    private $fileGenerator;
25
    /**
26
     * @var FunctionRepository
27
     */
28
    private $functions;
29
    /**
30
     * @var Interfaces\FileSystem
31
     */
32
    private $fileSystem;
33
34
    /**
35
     * FileEditor constructor.
36
     * @param FileGenerator       $fileGenerator
37
     * @param FunctionRepository  $functionRepository
38
     * @param FileSystemInterface $fileSystem
39
     */
40
    public function __construct(
41
        FileGenerator $fileGenerator,
42
        FunctionRepository $functionRepository,
43
        FileSystemInterface $fileSystem
44
    ) {
45
        $this->fileGenerator = $fileGenerator;
46
        $this->functions = $functionRepository;
47
        $this->fileSystem = $fileSystem;
48
    }
49
50
    /**
51
     * @param $path
52
     * @return void
53
     */
54
    public function addBackslashes($path)
55
    {
56
        $generator = $this->fileGenerator->fromReflectedFileName($path);
57
        $source = explode("\n", $generator->getSourceContent());
58
        $tokens = token_get_all(file_get_contents($path));
59
60
        $previousToken = \null;
61
        $functions = $this->getReplaceableFunctions($generator);
62
        $constants = $this->getDefinedConstants();
63
64
        foreach ($tokens as $token) {
65
            if (!is_array($token)) {
66
                $tempToken = $token;
67
                $token = [0 => 0, 1 => $tempToken, 2 => 0];
68
            }
69
70
            if ($token[0] == \T_STRING) {
71
                $line = $token[2];
72
73
                if ($this->isBackslashable($functions, $token, $previousToken, $constants)) {
0 ignored issues
show
Bug introduced by
It seems like $previousToken defined by \null on line 60 can also be of type null; however, NilPortugues\BackslashFi...itor::isBackslashable() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
74
                    $source[$line-1] = str_replace($token[1], '\\'.$token[1], $source[$line-1]);
75
                }
76
            }
77
78
            $previousToken = $token;
79
        }
80
81
        $source = $this->applyFinalFixes($source);
0 ignored issues
show
Documentation introduced by
$source is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
82
83
        $this->fileSystem->writeFile($path, $source);
84
    }
85
86
87
    public function removeBackslashes($path)
88
    {
89
        $generator = $this->fileGenerator->fromReflectedFileName($path);
90
        $source = explode("\n", $generator->getSourceContent());
0 ignored issues
show
Unused Code introduced by
$source is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
91
        $tokens = token_get_all(file_get_contents($path));
0 ignored issues
show
Unused Code introduced by
$tokens is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
92
93
    }
94
95
    /**
96
     * If a method exists under a namespace and has been aliased, or has been imported, don't replace.
97
     *
98
     * @param FileGenerator $generator
99
     * @param array         $functions
100
     *
101
     * @return array
102
     */
103
    private function removeUseFunctionsFromBackslashing(FileGenerator $generator, array $functions)
104
    {
105
        foreach ($generator->getUses() as $namespacedFunction) {
106
            list($functionOrClass) = $namespacedFunction;
107
108
            if (\function_exists($functionOrClass)) {
109
                $function = \explode("\\", $functionOrClass);
110
                $function = \array_pop($function);
111
112
                if (!empty($functions[$function])) {
113
                    unset($functions[$function]);
114
                }
115
            }
116
        }
117
118
        return $functions;
119
    }
120
121
    /**
122
     * @return array
123
     */
124
    private function getDefinedConstants()
125
    {
126
        if (empty(self::$constants)) {
127
            self::$constants = \array_keys(\get_defined_constants(false));
128
            $c = array_values(self::$constants);
129
            self::$constants = array_combine($c, $c);
130
        }
131
132
        return self::$constants;
133
    }
134
135
    /**
136
     * @param $generator
137
     * @return array
138
     */
139
    protected function getReplaceableFunctions($generator)
140
    {
141
        $functions = $this->functions->getBackslashableFunctions();
142
        $functions = $this->removeUseFunctionsFromBackslashing($generator, $functions);
143
144
        return $functions;
145
    }
146
147
    /**
148
     * @param string $source
149
     *
150
     * @return string
151
     */
152
    private function applyFinalFixes($source)
153
    {
154
        $source = implode("\n", $source);
155
        $source = str_replace("function \\", "function ", $source);
156
        $source = str_replace("const \\", "const ", $source);
157
        $source = str_replace("::\\", "::", $source);
158
159
        return (string) $source;
160
    }
161
162
    /**
163
     * @param $constants
164
     * @param $token
165
     * @param $previousToken
166
     *
167
     * @return bool
168
     */
169
    private function isConstant(array &$constants, array &$token, array &$previousToken)
170
    {
171
        return !empty($constants[strtoupper($token[1])]) && $previousToken[0] != \T_NAMESPACE;
172
    }
173
174
    /**
175
     * @param array $functions
176
     * @param array $token
177
     * @param array $previousToken
178
     *
179
     * @return bool
180
     */
181
    private function isFunction(array &$functions, array &$token, array &$previousToken)
182
    {
183
        return !empty($functions[$token[1]])
184
        && $previousToken[0] != \T_NAMESPACE
185
        && $previousToken[0] != \T_OBJECT_OPERATOR;
186
    }
187
188
    /**
189
     * @param array $functions
190
     * @param array $token
191
     * @param array $previousToken
192
     * @param array $constants
193
     *
194
     * @return bool
195
     */
196
    private function isBackslashable(array &$functions, array &$token, array &$previousToken, array &$constants)
197
    {
198
        return $this->isFunction($functions, $token, $previousToken)
199
        || ($this->isConstant($constants, $token, $previousToken));
200
    }
201
}
202