Passed
Push — master ( f01d00...2f7647 )
by Nicolaas
03:27
created

SearchAndReplace::setCheckReplacementIssues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Sunnysideup\UpgradeToSilverstripe4\Tasks\IndividualTasks;
4
5
use Sunnysideup\UpgradeToSilverstripe4\Api\SearchAndReplaceAPI;
6
use Sunnysideup\UpgradeToSilverstripe4\ReplacementData\LoadReplacementData;
7
8
use Sunnysideup\UpgradeToSilverstripe4\Tasks\Task;
9
10
/**
11
 * Replaces a bunch of code snippets in preparation of the upgrade.
12
 * Controversial replacements will be replaced with a comment
13
 * next to it so you can review replacements easily.
14
 */
15
class SearchAndReplace extends Task
16
{
17
    protected $taskStep = 's30';
18
19
    protected $debug = false;
20
21
    protected $checkReplacementIssues = false;
22
23
    protected $replacementHeader = 'upgrade to SS4';
24
25
    protected $ignoreFolderArray = [
26
        '.git',
27
    ];
28
29
    protected $alternativePathForReplacementData = '';
30
31
    public function getTitle()
32
    {
33
        return 'Search and Replace';
34
    }
35
36
    public function getDescription()
37
    {
38
        return '
39
            Replaces a bunch of code snippets in preparation of the upgrade.
40
            Controversial replacements will be replaced with a comment
41
            next to it so you can review replacements easily.';
42
    }
43
44
    public function setCheckReplacementIssues($b)
45
    {
46
        $this->checkReplacementIssues = $b;
47
48
        return $this;
49
    }
50
51
    public function setIgnoreFolderArray($a)
52
    {
53
        $this->ignoreFolderArray = $a;
54
55
        return $this;
56
    }
57
58
    public function setAlternativePathForReplacementData($s)
59
    {
60
        $this->alternativePathForReplacementData = $s;
61
62
        return $this;
63
    }
64
65
    public function runActualTask($params = [])
66
    {
67
        if ($this->checkReplacementIssues) {
68
            $this->checkReplacementDataIssues();
69
        }
70
71
        //replacement data
72
        $replacementDataObject = new LoadReplacementData(
73
            $this->mu(),
74
            $this->alternativePathForReplacementData,
75
            $this->params
76
        );
77
        $replacementArray = $replacementDataObject->getReplacementArrays();
78
79
        if ($this->debug) {
80
            $this->mu()->colourPrint(print_r($replacementArray, 1));
81
        }
82
83
        //replace API
84
        foreach ($this->mu()->getExistingModuleDirLocationsWithThemeFolders() as $moduleOrThemeDir) {
85
            $textSearchMachine = new SearchAndReplaceAPI($moduleOrThemeDir);
86
            $textSearchMachine->setIsReplacingEnabled(true);
87
            $textSearchMachine->addToIgnoreFolderArray($this->ignoreFolderArray);
88
89
            foreach ($replacementArray as $path => $pathArray) {
90
                $path = $moduleOrThemeDir . '/' . $path ?: '';
91
                $path = $this->mu()->checkIfPathExistsAndCleanItUp($path);
92
                if (! file_exists($path)) {
93
                    $this->mu()->colourPrint("SKIPPING ${path}");
94
                } else {
95
                    $textSearchMachine->setSearchPath($path);
96
                    foreach ($pathArray as $extension => $extensionArray) {
97
                        $textSearchMachine->setExtensions(explode('|', $extension)); //setting extensions to search files within
98
                        $this->mu()->colourPrint(
99
                            "++++++++++++++++++++++++++++++++++++\n" .
100
                            "CHECKING\n" .
101
                            "IN ${path}\n" .
102
                            "FOR ${extension} FILES\n" .
103
                            'BASE ' . $moduleOrThemeDir . "\n" .
104
                            "++++++++++++++++++++++++++++++++++++\n"
105
                        );
106
                        foreach ($extensionArray as $find => $findDetails) {
107
                            $replace = isset($findDetails['R']) ? $findDetails['R'] : $find;
108
                            $comment = isset($findDetails['C']) ? $findDetails['C'] : '';
109
                            $ignoreCase = isset($findDetails['I']) ? $findDetails['I'] : false;
110
                            $caseSensitive = ! $ignoreCase;
111
                            //$replace = $replaceArray[1]; unset($replaceArray[1]);
112
                            //$fullReplacement = (isset($replaceArray[2]) ? "/* ".$replaceArray[2]." */\n" : "").$replaceArray[1];
113
                            $fullReplacement = $replace;
114
                            $isStraightReplace = true;
115
                            if ($comment) {
116
                                $isStraightReplace = false;
117
                            }
118
                            if (! $find) {
119
                                user_error("no find is specified, replace is: ${replace}");
120
                            }
121
                            if (! $fullReplacement) {
122
                                user_error("no replace is specified, find is: ${find}. We suggest setting your final replace to a single space if you would like to replace with NOTHING.");
123
                            }
124
                            $replaceKey = $isStraightReplace ? 'BASIC' : 'COMPLEX';
125
126
                            $textSearchMachine->setSearchKey($find, $caseSensitive, $replaceKey);
127
                            $textSearchMachine->setReplacementKey($fullReplacement);
128
                            if ($comment) {
129
                                $textSearchMachine->setComment($comment);
130
                            }
131
                            $textSearchMachine->setReplacementHeader($this->replacementHeader);
132
                            $textSearchMachine->startSearchAndReplace();
133
                        }
134
                        $replacements = $textSearchMachine->showFormattedSearchTotals();
135
                        if ($replacements) {
136
                        } else {
137
                            //flush output anyway!
138
                            $this->mu()->colourPrint("No replacements for  ${extension}");
139
                        }
140
                        $this->mu()->colourPrint($textSearchMachine->getOutput());
141
                    }
142
                }
143
            }
144
        }
145
    }
146
147
    protected function hasCommitAndPush()
148
    {
149
        return true;
150
    }
151
152
    /**
153
     * 1. check that one find is not used twice:
154
     * find can be found 2x
155
     */
156
    private function checkReplacementDataIssues()
157
    {
158
        $replacementDataObject = new LoadReplacementData(
159
            $this->mu(),
160
            $this->alternativePathForReplacementData,
161
            $this->params
162
        );
163
        $arr = $replacementDataObject->getReplacementArrays(null);
0 ignored issues
show
Unused Code introduced by
The call to Sunnysideup\UpgradeToSil...:getReplacementArrays() has too many arguments starting with null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
        /** @scrutinizer ignore-call */ 
164
        $arr = $replacementDataObject->getReplacementArrays(null);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Unused Code introduced by
The assignment to $arr is dead and can be removed.
Loading history...
164
        $arrTos = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $arrTos is dead and can be removed.
Loading history...
165
        $arrLanguages = $replacementDataObject->getLanguages();
166
        $fullFindArray = $replacementDataObject->getFlatFindArray();
167
        $fullReplaceArray = $replacementDataObject->getFlatReplacedArray();
168
169
        //1. check that one find may not stop another replacement.
170
        foreach ($arrLanguages as $language) {
171
            if (! isset($fullFindArray[$language])) {
172
                continue;
173
            }
174
            unset($keyOuterDoneSoFar);
175
            $keyOuterDoneSoFar = [];
176
            foreach ($fullFindArray[$language] as $keyOuter => $findStringOuter) {
177
                $keyOuterDoneSoFar[$keyOuter] = true;
178
                foreach ($fullFindArray[$language] as $keyInner => $findStringInner) {
179
                    if (! isset($keyOuterDoneSoFar[$keyInner])) {
180
                        if ($keyOuter !== $keyInner) {
181
                            $findStringOuterReplaced = str_replace($findStringInner, '...', $findStringOuter);
182
                            if ($findStringOuter === $findStringInner || $findStringOuterReplaced !== $findStringOuter) {
183
                                $this->mu()->colourPrint("
184
ERROR in ${language}: \t\t we are trying to find the same thing twice (A and B)
185
---- A: (${keyOuter}): \t\t ${findStringOuter}
186
---- B: (${keyInner}): \t\t ${findStringInner}");
187
                            }
188
                        }
189
                    }
190
                }
191
            }
192
        }
193
194
        //2. check that a replacement is not mentioned before the it is being replaced
195
        foreach ($arrLanguages as $language) {
196
            if (! isset($fullReplaceArray[$language])) {
197
                continue;
198
            }
199
            unset($keyOuterDoneSoFar);
200
            $keyOuterDoneSoFar = [];
201
            foreach ($fullReplaceArray[$language] as $keyOuter => $findStringOuter) {
202
                $keyOuterDoneSoFar[$keyOuter] = true;
203
                foreach ($fullFindArray[$language] as $keyInner => $findStringInner) {
204
                    if (isset($keyOuterDoneSoFar[$keyInner])) {
205
                        if ($keyOuter !== $keyInner) {
206
                            $findStringOuterReplaced = str_replace($findStringInner, '...', $findStringOuter);
207
                            if ($findStringOuter === $findStringInner || $findStringOuterReplaced !== $findStringOuter) {
208
                                $this->mu()->colourPrint("
209
ERROR in ${language}: \t\t there is a replacement (A) that was earlier tried to be found (B).
210
---- A: (${keyOuter}): \t\t ${findStringOuter}
211
---- B: (${keyInner}): \t\t ${findStringInner}");
212
                            }
213
                        }
214
                    }
215
                }
216
            }
217
        }
218
        $this->mu()->colourPrint('');
219
    }
220
}
221