Passed
Push — master ( 602b13...8fad06 )
by Nicolaas
02:10
created

SearchAndReplace::setCommitAndPush()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
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\LoadReplacementData;
6
use Sunnysideup\UpgradeToSilverstripe4\Api\SearchAndReplaceAPI;
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
    /**
20
     * for debugging purposes
21
     * @var bool
22
     */
23
    protected $debug = false;
24
25
    /**
26
     * check if there are double-ups in replacement.
27
     * @var bool
28
     */
29
    protected $checkReplacementIssues = false;
30
31
    /**
32
     * string used to show issues in replacement in the actual code being replaced.
33
     *
34
     * @var string
35
     */
36
    protected $replacementHeader = 'automated upgrade';
37
38
    /**
39
     * folder containing the replacement file
40
     *
41
     * @var string
42
     */
43
    protected $folderContainingReplacementData = '';
44
45
    /**
46
     * list of folders to ignore in search and replace
47
     * @var array
48
     */
49
    protected $ignoreFolderArray = [
50
        '.git',
51
        '.svn',
52
    ];
53
54
    /**
55
     * the names of the folder that contains the data we need
56
     * e.g. SS4 / SS37
57
     * IMPORTANT!
58
     *
59
     * @var array
60
     */
61
    protected $sourceFolders = [
62
        'SS4',
63
    ];
64
65
    protected $commitAndPush = true;
66
67
    public function getTitle()
68
    {
69
        return 'Search and Replace';
70
    }
71
72
    public function getDescription()
73
    {
74
        return '
75
            Replaces a bunch of code snippets in preparation of the upgrade.
76
            Controversial replacements will be replaced with a comment
77
            next to it so you can review replacements easily.';
78
    }
79
80
    public function setCheckReplacementIssues(bool $b)
81
    {
82
        $this->checkReplacementIssues = $b;
83
84
        return $this;
85
    }
86
87
    public function setIgnoreFolderArray(array $a)
88
    {
89
        $this->ignoreFolderArray = $a;
90
91
        return $this;
92
    }
93
94
    public function setCommitAndPush(bool $b)
95
    {
96
        $this->commitAndPush = $b;
97
98
        return $this;
99
    }
100
101
    public function setFolderContainingReplacementData(string $s)
102
    {
103
        $this->folderContainingReplacementData = $s;
104
105
        return $this;
106
    }
107
108
    public function setSourceFolders(array $a)
109
    {
110
        $this->sourceFolders = $a;
111
112
        return $this;
113
    }
114
115
    public function runActualTask($params = [])
116
    {
117
118
        //replacement data
119
        $replacementDataObjects = $this->getReplacementDataObjects();
120
        foreach ($replacementDataObjects as $replacementDataObject) {
121
            if ($this->checkReplacementIssues) {
122
                $this->checkReplacementDataIssues($replacementDataObject);
123
            }
124
125
            $replacementArray = $replacementDataObject->getReplacementArrays();
126
127
            if ($this->debug) {
128
                $this->mu()->colourPrint($replacementArray);
0 ignored issues
show
Bug introduced by
It seems like colourPrint() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

128
                $this->mu()->/** @scrutinizer ignore-call */ colourPrint($replacementArray);
Loading history...
129
            }
130
131
            //replace API
132
133
            foreach ($this->mu()->getExistingModuleDirLocationsWithThemeFolders() as $moduleOrThemeDir) {
0 ignored issues
show
Bug introduced by
It seems like getExistingModuleDirLocationsWithThemeFolders() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

133
            foreach ($this->mu()->/** @scrutinizer ignore-call */ getExistingModuleDirLocationsWithThemeFolders() as $moduleOrThemeDir) {
Loading history...
134
                $textSearchMachine = new SearchAndReplaceAPI($moduleOrThemeDir);
135
                $textSearchMachine->setIsReplacingEnabled(true);
136
                $textSearchMachine->addToIgnoreFolderArray($this->ignoreFolderArray);
137
138
                foreach ($replacementArray as $path => $pathArray) {
139
                    $path = $moduleOrThemeDir . '/' . $path ?: '';
140
                    $path = $this->mu()->checkIfPathExistsAndCleanItUp($path);
0 ignored issues
show
Bug introduced by
It seems like checkIfPathExistsAndCleanItUp() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

140
                    $path = $this->mu()->/** @scrutinizer ignore-call */ checkIfPathExistsAndCleanItUp($path);
Loading history...
141
                    if (! file_exists($path)) {
142
                        $this->mu()->colourPrint("SKIPPING ${path}");
143
                    } else {
144
                        $textSearchMachine->setSearchPath($path);
145
                        foreach ($pathArray as $extension => $extensionArray) {
146
                            //setting extensions to search files within
147
                            $textSearchMachine->setExtensions(explode('|', $extension));
148
                            $this->mu()->colourPrint(
149
                                "++++++++++++++++++++++++++++++++++++\n" .
150
                                "CHECKING\n" .
151
                                "IN ${path}\n" .
152
                                "FOR ${extension} FILES\n" .
153
                                'BASE ' . $moduleOrThemeDir . "\n" .
154
                                "++++++++++++++++++++++++++++++++++++\n"
155
                            );
156
                            foreach ($extensionArray as $find => $findDetails) {
157
                                $replace = isset($findDetails['R']) ? $findDetails['R'] : $find;
158
                                $comment = isset($findDetails['C']) ? $findDetails['C'] : '';
159
                                $ignoreCase = isset($findDetails['I']) ? $findDetails['I'] : false;
160
                                $caseSensitive = ! $ignoreCase;
161
                                //$replace = $replaceArray[1]; unset($replaceArray[1]);
162
                                //$fullReplacement =
163
                                //   (isset($replaceArray[2]) ? "/* ".$replaceArray[2]." */\n" : "").$replaceArray[1];
164
                                $fullReplacement = $replace;
165
                                $isStraightReplace = true;
166
                                if ($comment) {
167
                                    $isStraightReplace = false;
168
                                }
169
                                if (! $find) {
170
                                    user_error("no find is specified, replace is: ${replace}");
171
                                }
172
                                if (! $fullReplacement) {
173
                                    user_error('
174
                                        No replace is specified, find is: ${find}.
175
                                        We suggest setting your final replace to a single space if
176
                                        you would like to replace with NOTHING.
177
                                    ');
178
                                }
179
                                $replaceKey = $isStraightReplace ? 'BASIC' : 'COMPLEX';
180
181
                                $textSearchMachine->setSearchKey($find, $caseSensitive, $replaceKey);
182
                                $textSearchMachine->setReplacementKey($fullReplacement);
183
                                if ($comment) {
184
                                    $textSearchMachine->setComment($comment);
185
                                }
186
                                $textSearchMachine->setReplacementHeader($this->replacementHeader);
187
                                $textSearchMachine->startSearchAndReplace();
188
                            }
189
                            $replacements = $textSearchMachine->showFormattedSearchTotals();
190
                            if ($replacements) {
191
                            } else {
192
                                //flush output anyway!
193
                                $this->mu()->colourPrint("No replacements for  ${extension}");
194
                            }
195
                            $this->mu()->colourPrint($textSearchMachine->getOutput());
196
                        }
197
                    }
198
                }
199
            }
200
        }
201
    }
202
203
    protected function hasCommitAndPush()
204
    {
205
        return $this->commitAndPush;
206
    }
207
208
    protected function getReplacementDataObjects(): array
209
    {
210
        $array = [];
211
        foreach ($this->sourceFolders as $sourceFolder) {
212
            $array[] = new LoadReplacementData(
213
                $this->mu(),
214
                $this->folderContainingReplacementData,
215
                $sourceFolder
216
            );
217
        }
218
219
        return $array;
220
    }
221
222
    /**
223
     * 1. check that one find is not used twice:
224
     * find can be found 2x
225
     * @param mixed $replacementDataObject
226
     */
227
    private function checkReplacementDataIssues($replacementDataObject)
228
    {
229
        $replacementDataObject->getReplacementArrays(null);
230
        $arrLanguages = $replacementDataObject->getLanguages();
231
        $fullFindArray = $replacementDataObject->getFlatFindArray();
232
        $fullReplaceArray = $replacementDataObject->getFlatReplacedArray();
233
234
        //1. check that one find may not stop another replacement.
235
        foreach ($arrLanguages as $language) {
236
            if (! isset($fullFindArray[$language])) {
237
                continue;
238
            }
239
            unset($keyOuterDoneSoFar);
240
            $keyOuterDoneSoFar = [];
241
            foreach ($fullFindArray[$language] as $keyOuter => $findStringOuter) {
242
                $keyOuterDoneSoFar[$keyOuter] = true;
243
                foreach ($fullFindArray[$language] as $keyInner => $findStringInner) {
244
                    if (! isset($keyOuterDoneSoFar[$keyInner])) {
245
                        if ($keyOuter !== $keyInner) {
246
                            $findStringOuterReplaced = str_replace($findStringInner, '...', $findStringOuter);
247
                            if ($findStringOuter === $findStringInner || $findStringOuterReplaced !== $findStringOuter) {
248
                                $this->mu()->colourPrint("
249
ERROR in ${language}: \t\t we are trying to find the same thing twice (A and B)
250
---- A: (${keyOuter}): \t\t ${findStringOuter}
251
---- B: (${keyInner}): \t\t ${findStringInner}");
252
                            }
253
                        }
254
                    }
255
                }
256
            }
257
        }
258
259
        //2. check that a replacement is not mentioned before the it is being replaced
260
        foreach ($arrLanguages as $language) {
261
            if (! isset($fullReplaceArray[$language])) {
262
                continue;
263
            }
264
            unset($keyOuterDoneSoFar);
265
            $keyOuterDoneSoFar = [];
266
            foreach ($fullReplaceArray[$language] as $keyOuter => $findStringOuter) {
267
                $keyOuterDoneSoFar[$keyOuter] = true;
268
                foreach ($fullFindArray[$language] as $keyInner => $findStringInner) {
269
                    if (isset($keyOuterDoneSoFar[$keyInner])) {
270
                        if ($keyOuter !== $keyInner) {
271
                            $findStringOuterReplaced = str_replace($findStringInner, '...', $findStringOuter);
272
                            if ($findStringOuter === $findStringInner || $findStringOuterReplaced !== $findStringOuter) {
273
                                $this->mu()->colourPrint("
274
ERROR in ${language}: \t\t there is a replacement (A) that was earlier tried to be found (B).
275
---- A: (${keyOuter}): \t\t ${findStringOuter}
276
---- B: (${keyInner}): \t\t ${findStringInner}");
277
                            }
278
                        }
279
                    }
280
                }
281
            }
282
        }
283
        $this->mu()->colourPrint('');
284
    }
285
}
286