Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * A helper class for fixing errors.
|
Chris@0
|
4 *
|
Chris@0
|
5 * PHP version 5
|
Chris@0
|
6 *
|
Chris@0
|
7 * @category PHP
|
Chris@0
|
8 * @package PHP_CodeSniffer
|
Chris@0
|
9 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
10 * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
11 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
12 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
13 */
|
Chris@0
|
14
|
Chris@0
|
15 /**
|
Chris@0
|
16 * A helper class for fixing errors.
|
Chris@0
|
17 *
|
Chris@0
|
18 * Provides helper functions that act upon a token array and modify the file
|
Chris@0
|
19 * content.
|
Chris@0
|
20 *
|
Chris@0
|
21 * @category PHP
|
Chris@0
|
22 * @package PHP_CodeSniffer
|
Chris@0
|
23 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@0
|
24 * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@0
|
25 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@0
|
26 * @version Release: @package_version@
|
Chris@0
|
27 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
28 */
|
Chris@0
|
29 class PHP_CodeSniffer_Fixer
|
Chris@0
|
30 {
|
Chris@0
|
31
|
Chris@0
|
32 /**
|
Chris@0
|
33 * Is the fixer enabled and fixing a file?
|
Chris@0
|
34 *
|
Chris@0
|
35 * Sniffs should check this value to ensure they are not
|
Chris@0
|
36 * doing extra processing to prepare for a fix when fixing is
|
Chris@0
|
37 * not required.
|
Chris@0
|
38 *
|
Chris@0
|
39 * @var boolean
|
Chris@0
|
40 */
|
Chris@0
|
41 public $enabled = false;
|
Chris@0
|
42
|
Chris@0
|
43 /**
|
Chris@0
|
44 * The number of times we have looped over a file.
|
Chris@0
|
45 *
|
Chris@0
|
46 * @var int
|
Chris@0
|
47 */
|
Chris@0
|
48 public $loops = 0;
|
Chris@0
|
49
|
Chris@0
|
50 /**
|
Chris@0
|
51 * The file being fixed.
|
Chris@0
|
52 *
|
Chris@0
|
53 * @var PHP_CodeSniffer_File
|
Chris@0
|
54 */
|
Chris@0
|
55 private $_currentFile = null;
|
Chris@0
|
56
|
Chris@0
|
57 /**
|
Chris@0
|
58 * The list of tokens that make up the file contents.
|
Chris@0
|
59 *
|
Chris@0
|
60 * This is a simplified list which just contains the token content and nothing
|
Chris@0
|
61 * else. This is the array that is updated as fixes are made, not the file's
|
Chris@0
|
62 * token array. Imploding this array will give you the file content back.
|
Chris@0
|
63 *
|
Chris@0
|
64 * @var array(int => string)
|
Chris@0
|
65 */
|
Chris@0
|
66 private $_tokens = array();
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * A list of tokens that have already been fixed.
|
Chris@0
|
70 *
|
Chris@0
|
71 * We don't allow the same token to be fixed more than once each time
|
Chris@0
|
72 * through a file as this can easily cause conflicts between sniffs.
|
Chris@0
|
73 *
|
Chris@0
|
74 * @var array(int)
|
Chris@0
|
75 */
|
Chris@0
|
76 private $_fixedTokens = array();
|
Chris@0
|
77
|
Chris@0
|
78 /**
|
Chris@0
|
79 * The last value of each fixed token.
|
Chris@0
|
80 *
|
Chris@0
|
81 * If a token is being "fixed" back to its last value, the fix is
|
Chris@0
|
82 * probably conflicting with another.
|
Chris@0
|
83 *
|
Chris@0
|
84 * @var array(int => string)
|
Chris@0
|
85 */
|
Chris@0
|
86 private $_oldTokenValues = array();
|
Chris@0
|
87
|
Chris@0
|
88 /**
|
Chris@0
|
89 * A list of tokens that have been fixed during a changeset.
|
Chris@0
|
90 *
|
Chris@0
|
91 * All changes in changeset must be able to be applied, or else
|
Chris@0
|
92 * the entire changeset is rejected.
|
Chris@0
|
93 *
|
Chris@0
|
94 * @var array()
|
Chris@0
|
95 */
|
Chris@0
|
96 private $_changeset = array();
|
Chris@0
|
97
|
Chris@0
|
98 /**
|
Chris@0
|
99 * Is there an open changeset.
|
Chris@0
|
100 *
|
Chris@0
|
101 * @var boolean
|
Chris@0
|
102 */
|
Chris@0
|
103 private $_inChangeset = false;
|
Chris@0
|
104
|
Chris@0
|
105 /**
|
Chris@0
|
106 * Is the current fixing loop in conflict?
|
Chris@0
|
107 *
|
Chris@0
|
108 * @var boolean
|
Chris@0
|
109 */
|
Chris@0
|
110 private $_inConflict = false;
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * The number of fixes that have been performed.
|
Chris@0
|
114 *
|
Chris@0
|
115 * @var int
|
Chris@0
|
116 */
|
Chris@0
|
117 private $_numFixes = 0;
|
Chris@0
|
118
|
Chris@0
|
119
|
Chris@0
|
120 /**
|
Chris@0
|
121 * Starts fixing a new file.
|
Chris@0
|
122 *
|
Chris@0
|
123 * @param PHP_CodeSniffer_File $phpcsFile The file being fixed.
|
Chris@0
|
124 *
|
Chris@0
|
125 * @return void
|
Chris@0
|
126 */
|
Chris@0
|
127 public function startFile($phpcsFile)
|
Chris@0
|
128 {
|
Chris@0
|
129 $this->_currentFile = $phpcsFile;
|
Chris@0
|
130 $this->_numFixes = 0;
|
Chris@0
|
131 $this->_fixedTokens = array();
|
Chris@0
|
132
|
Chris@0
|
133 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
134 $this->_tokens = array();
|
Chris@0
|
135 foreach ($tokens as $index => $token) {
|
Chris@0
|
136 if (isset($token['orig_content']) === true) {
|
Chris@0
|
137 $this->_tokens[$index] = $token['orig_content'];
|
Chris@0
|
138 } else {
|
Chris@0
|
139 $this->_tokens[$index] = $token['content'];
|
Chris@0
|
140 }
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 }//end startFile()
|
Chris@0
|
144
|
Chris@0
|
145
|
Chris@0
|
146 /**
|
Chris@0
|
147 * Attempt to fix the file by processing it until no fixes are made.
|
Chris@0
|
148 *
|
Chris@0
|
149 * @return boolean
|
Chris@0
|
150 */
|
Chris@0
|
151 public function fixFile()
|
Chris@0
|
152 {
|
Chris@0
|
153 $fixable = $this->_currentFile->getFixableCount();
|
Chris@0
|
154 if ($fixable === 0) {
|
Chris@0
|
155 // Nothing to fix.
|
Chris@0
|
156 return false;
|
Chris@0
|
157 }
|
Chris@0
|
158
|
Chris@0
|
159 $stdin = false;
|
Chris@0
|
160 $cliValues = $this->_currentFile->phpcs->cli->getCommandLineValues();
|
Chris@0
|
161 if (empty($cliValues['files']) === true) {
|
Chris@0
|
162 $stdin = true;
|
Chris@0
|
163 }
|
Chris@0
|
164
|
Chris@0
|
165 $this->enabled = true;
|
Chris@0
|
166
|
Chris@0
|
167 $this->loops = 0;
|
Chris@0
|
168 while ($this->loops < 50) {
|
Chris@0
|
169 ob_start();
|
Chris@0
|
170
|
Chris@0
|
171 // Only needed once file content has changed.
|
Chris@0
|
172 $contents = $this->getContents();
|
Chris@0
|
173
|
Chris@0
|
174 if (PHP_CODESNIFFER_VERBOSITY > 2) {
|
Chris@0
|
175 @ob_end_clean();
|
Chris@0
|
176 echo '---START FILE CONTENT---'.PHP_EOL;
|
Chris@0
|
177 $lines = explode($this->_currentFile->eolChar, $contents);
|
Chris@0
|
178 $max = strlen(count($lines));
|
Chris@0
|
179 foreach ($lines as $lineNum => $line) {
|
Chris@0
|
180 $lineNum++;
|
Chris@0
|
181 echo str_pad($lineNum, $max, ' ', STR_PAD_LEFT).'|'.$line.PHP_EOL;
|
Chris@0
|
182 }
|
Chris@0
|
183
|
Chris@0
|
184 echo '--- END FILE CONTENT ---'.PHP_EOL;
|
Chris@0
|
185 ob_start();
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 $this->_inConflict = false;
|
Chris@0
|
189 $this->_currentFile->refreshTokenListeners();
|
Chris@0
|
190 $this->_currentFile->start($contents);
|
Chris@0
|
191 ob_end_clean();
|
Chris@0
|
192
|
Chris@0
|
193 $this->loops++;
|
Chris@0
|
194
|
Chris@0
|
195 if (PHP_CODESNIFFER_CBF === true && $stdin === false) {
|
Chris@0
|
196 echo "\r".str_repeat(' ', 80)."\r";
|
Chris@0
|
197 echo "\t=> Fixing file: $this->_numFixes/$fixable violations remaining [made $this->loops pass";
|
Chris@0
|
198 if ($this->loops > 1) {
|
Chris@0
|
199 echo 'es';
|
Chris@0
|
200 }
|
Chris@0
|
201
|
Chris@0
|
202 echo ']... ';
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 if ($this->_numFixes === 0 && $this->_inConflict === false) {
|
Chris@0
|
206 // Nothing left to do.
|
Chris@0
|
207 break;
|
Chris@0
|
208 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
209 echo "\t* fixed $this->_numFixes violations, starting loop ".($this->loops + 1).' *'.PHP_EOL;
|
Chris@0
|
210 }
|
Chris@0
|
211 }//end while
|
Chris@0
|
212
|
Chris@0
|
213 $this->enabled = false;
|
Chris@0
|
214
|
Chris@0
|
215 if ($this->_numFixes > 0) {
|
Chris@0
|
216 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
217 @ob_end_clean();
|
Chris@0
|
218 echo "\t*** Reached maximum number of loops with $this->_numFixes violations left unfixed ***".PHP_EOL;
|
Chris@0
|
219 ob_start();
|
Chris@0
|
220 }
|
Chris@0
|
221
|
Chris@0
|
222 return false;
|
Chris@0
|
223 }
|
Chris@0
|
224
|
Chris@0
|
225 return true;
|
Chris@0
|
226
|
Chris@0
|
227 }//end fixFile()
|
Chris@0
|
228
|
Chris@0
|
229
|
Chris@0
|
230 /**
|
Chris@0
|
231 * Generates a text diff of the original file and the new content.
|
Chris@0
|
232 *
|
Chris@0
|
233 * @param string $filePath Optional file path to diff the file against.
|
Chris@0
|
234 * If not specified, the original version of the
|
Chris@0
|
235 * file will be used.
|
Chris@0
|
236 * @param boolean $colors Print colored output or not.
|
Chris@0
|
237 *
|
Chris@0
|
238 * @return string
|
Chris@0
|
239 */
|
Chris@0
|
240 public function generateDiff($filePath=null, $colors=true)
|
Chris@0
|
241 {
|
Chris@0
|
242 if ($filePath === null) {
|
Chris@0
|
243 $filePath = $this->_currentFile->getFilename();
|
Chris@0
|
244 }
|
Chris@0
|
245
|
Chris@0
|
246 $cwd = getcwd().DIRECTORY_SEPARATOR;
|
Chris@0
|
247 if (strpos($filePath, $cwd) === 0) {
|
Chris@0
|
248 $filename = substr($filePath, strlen($cwd));
|
Chris@0
|
249 } else {
|
Chris@0
|
250 $filename = $filePath;
|
Chris@0
|
251 }
|
Chris@0
|
252
|
Chris@0
|
253 $contents = $this->getContents();
|
Chris@0
|
254
|
Chris@0
|
255 if (function_exists('sys_get_temp_dir') === true) {
|
Chris@0
|
256 // This is needed for HHVM support, but only available from 5.2.1.
|
Chris@0
|
257 $tempName = tempnam(sys_get_temp_dir(), 'phpcs-fixer');
|
Chris@0
|
258 $fixedFile = fopen($tempName, 'w');
|
Chris@0
|
259 } else {
|
Chris@0
|
260 $fixedFile = tmpfile();
|
Chris@0
|
261 $data = stream_get_meta_data($fixedFile);
|
Chris@0
|
262 $tempName = $data['uri'];
|
Chris@0
|
263 }
|
Chris@0
|
264
|
Chris@0
|
265 fwrite($fixedFile, $contents);
|
Chris@0
|
266
|
Chris@0
|
267 // We must use something like shell_exec() because whitespace at the end
|
Chris@0
|
268 // of lines is critical to diff files.
|
Chris@0
|
269 $filename = escapeshellarg($filename);
|
Chris@0
|
270 $cmd = "diff -u -L$filename -LPHP_CodeSniffer $filename \"$tempName\"";
|
Chris@0
|
271
|
Chris@0
|
272 $diff = shell_exec($cmd);
|
Chris@0
|
273
|
Chris@0
|
274 fclose($fixedFile);
|
Chris@0
|
275 if (is_file($tempName) === true) {
|
Chris@0
|
276 unlink($tempName);
|
Chris@0
|
277 }
|
Chris@0
|
278
|
Chris@0
|
279 if ($colors === false) {
|
Chris@0
|
280 return $diff;
|
Chris@0
|
281 }
|
Chris@0
|
282
|
Chris@0
|
283 $diffLines = explode(PHP_EOL, $diff);
|
Chris@0
|
284 if (count($diffLines) === 1) {
|
Chris@0
|
285 // Seems to be required for cygwin.
|
Chris@0
|
286 $diffLines = explode("\n", $diff);
|
Chris@0
|
287 }
|
Chris@0
|
288
|
Chris@0
|
289 $diff = array();
|
Chris@0
|
290 foreach ($diffLines as $line) {
|
Chris@0
|
291 if (isset($line[0]) === true) {
|
Chris@0
|
292 switch ($line[0]) {
|
Chris@0
|
293 case '-':
|
Chris@0
|
294 $diff[] = "\033[31m$line\033[0m";
|
Chris@0
|
295 break;
|
Chris@0
|
296 case '+':
|
Chris@0
|
297 $diff[] = "\033[32m$line\033[0m";
|
Chris@0
|
298 break;
|
Chris@0
|
299 default:
|
Chris@0
|
300 $diff[] = $line;
|
Chris@0
|
301 }
|
Chris@0
|
302 }
|
Chris@0
|
303 }
|
Chris@0
|
304
|
Chris@0
|
305 $diff = implode(PHP_EOL, $diff);
|
Chris@0
|
306
|
Chris@0
|
307 return $diff;
|
Chris@0
|
308
|
Chris@0
|
309 }//end generateDiff()
|
Chris@0
|
310
|
Chris@0
|
311
|
Chris@0
|
312 /**
|
Chris@0
|
313 * Get a count of fixes that have been performed on the file.
|
Chris@0
|
314 *
|
Chris@0
|
315 * This value is reset every time a new file is started, or an existing
|
Chris@0
|
316 * file is restarted.
|
Chris@0
|
317 *
|
Chris@0
|
318 * @return int
|
Chris@0
|
319 */
|
Chris@0
|
320 public function getFixCount()
|
Chris@0
|
321 {
|
Chris@0
|
322 return $this->_numFixes;
|
Chris@0
|
323
|
Chris@0
|
324 }//end getFixCount()
|
Chris@0
|
325
|
Chris@0
|
326
|
Chris@0
|
327 /**
|
Chris@0
|
328 * Get the current content of the file, as a string.
|
Chris@0
|
329 *
|
Chris@0
|
330 * @return string
|
Chris@0
|
331 */
|
Chris@0
|
332 public function getContents()
|
Chris@0
|
333 {
|
Chris@0
|
334 $contents = implode($this->_tokens);
|
Chris@0
|
335 return $contents;
|
Chris@0
|
336
|
Chris@0
|
337 }//end getContents()
|
Chris@0
|
338
|
Chris@0
|
339
|
Chris@0
|
340 /**
|
Chris@0
|
341 * Get the current fixed content of a token.
|
Chris@0
|
342 *
|
Chris@0
|
343 * This function takes changesets into account so should be used
|
Chris@0
|
344 * instead of directly accessing the token array.
|
Chris@0
|
345 *
|
Chris@0
|
346 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
347 *
|
Chris@0
|
348 * @return string
|
Chris@0
|
349 */
|
Chris@0
|
350 public function getTokenContent($stackPtr)
|
Chris@0
|
351 {
|
Chris@0
|
352 if ($this->_inChangeset === true
|
Chris@0
|
353 && isset($this->_changeset[$stackPtr]) === true
|
Chris@0
|
354 ) {
|
Chris@0
|
355 return $this->_changeset[$stackPtr];
|
Chris@0
|
356 } else {
|
Chris@0
|
357 return $this->_tokens[$stackPtr];
|
Chris@0
|
358 }
|
Chris@0
|
359
|
Chris@0
|
360 }//end getTokenContent()
|
Chris@0
|
361
|
Chris@0
|
362
|
Chris@0
|
363 /**
|
Chris@0
|
364 * Start recording actions for a changeset.
|
Chris@0
|
365 *
|
Chris@0
|
366 * @return void
|
Chris@0
|
367 */
|
Chris@0
|
368 public function beginChangeset()
|
Chris@0
|
369 {
|
Chris@0
|
370 if ($this->_inConflict === true) {
|
Chris@0
|
371 return false;
|
Chris@0
|
372 }
|
Chris@0
|
373
|
Chris@0
|
374 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
375 $bt = debug_backtrace();
|
Chris@0
|
376 $sniff = $bt[1]['class'];
|
Chris@0
|
377 $line = $bt[0]['line'];
|
Chris@0
|
378
|
Chris@0
|
379 @ob_end_clean();
|
Chris@0
|
380 echo "\t=> Changeset started by $sniff (line $line)".PHP_EOL;
|
Chris@0
|
381 ob_start();
|
Chris@0
|
382 }
|
Chris@0
|
383
|
Chris@0
|
384 $this->_changeset = array();
|
Chris@0
|
385 $this->_inChangeset = true;
|
Chris@0
|
386
|
Chris@0
|
387 }//end beginChangeset()
|
Chris@0
|
388
|
Chris@0
|
389
|
Chris@0
|
390 /**
|
Chris@0
|
391 * Stop recording actions for a changeset, and apply logged changes.
|
Chris@0
|
392 *
|
Chris@0
|
393 * @return boolean
|
Chris@0
|
394 */
|
Chris@0
|
395 public function endChangeset()
|
Chris@0
|
396 {
|
Chris@0
|
397 if ($this->_inConflict === true) {
|
Chris@0
|
398 return false;
|
Chris@0
|
399 }
|
Chris@0
|
400
|
Chris@0
|
401 $this->_inChangeset = false;
|
Chris@0
|
402
|
Chris@0
|
403 $success = true;
|
Chris@0
|
404 $applied = array();
|
Chris@0
|
405 foreach ($this->_changeset as $stackPtr => $content) {
|
Chris@0
|
406 $success = $this->replaceToken($stackPtr, $content);
|
Chris@0
|
407 if ($success === false) {
|
Chris@0
|
408 break;
|
Chris@0
|
409 } else {
|
Chris@0
|
410 $applied[] = $stackPtr;
|
Chris@0
|
411 }
|
Chris@0
|
412 }
|
Chris@0
|
413
|
Chris@0
|
414 if ($success === false) {
|
Chris@0
|
415 // Rolling back all changes.
|
Chris@0
|
416 foreach ($applied as $stackPtr) {
|
Chris@0
|
417 $this->revertToken($stackPtr);
|
Chris@0
|
418 }
|
Chris@0
|
419
|
Chris@0
|
420 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
421 @ob_end_clean();
|
Chris@0
|
422 echo "\t=> Changeset failed to apply".PHP_EOL;
|
Chris@0
|
423 ob_start();
|
Chris@0
|
424 }
|
Chris@0
|
425 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
426 $fixes = count($this->_changeset);
|
Chris@0
|
427 @ob_end_clean();
|
Chris@0
|
428 echo "\t=> Changeset ended: $fixes changes applied".PHP_EOL;
|
Chris@0
|
429 ob_start();
|
Chris@0
|
430 }
|
Chris@0
|
431
|
Chris@0
|
432 $this->_changeset = array();
|
Chris@0
|
433
|
Chris@0
|
434 }//end endChangeset()
|
Chris@0
|
435
|
Chris@0
|
436
|
Chris@0
|
437 /**
|
Chris@0
|
438 * Stop recording actions for a changeset, and discard logged changes.
|
Chris@0
|
439 *
|
Chris@0
|
440 * @return void
|
Chris@0
|
441 */
|
Chris@0
|
442 public function rollbackChangeset()
|
Chris@0
|
443 {
|
Chris@0
|
444 $this->_inChangeset = false;
|
Chris@0
|
445 $this->_inConflict = false;
|
Chris@0
|
446
|
Chris@0
|
447 if (empty($this->_changeset) === false) {
|
Chris@0
|
448 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
449 $bt = debug_backtrace();
|
Chris@0
|
450 if ($bt[1]['class'] === 'PHP_CodeSniffer_Fixer') {
|
Chris@0
|
451 $sniff = $bt[2]['class'];
|
Chris@0
|
452 $line = $bt[1]['line'];
|
Chris@0
|
453 } else {
|
Chris@0
|
454 $sniff = $bt[1]['class'];
|
Chris@0
|
455 $line = $bt[0]['line'];
|
Chris@0
|
456 }
|
Chris@0
|
457
|
Chris@0
|
458 $numChanges = count($this->_changeset);
|
Chris@0
|
459
|
Chris@0
|
460 @ob_end_clean();
|
Chris@0
|
461 echo "\t\tR: $sniff (line $line) rolled back the changeset ($numChanges changes)".PHP_EOL;
|
Chris@0
|
462 echo "\t=> Changeset rolled back".PHP_EOL;
|
Chris@0
|
463 ob_start();
|
Chris@0
|
464 }
|
Chris@0
|
465
|
Chris@0
|
466 $this->_changeset = array();
|
Chris@0
|
467 }//end if
|
Chris@0
|
468
|
Chris@0
|
469 }//end rollbackChangeset()
|
Chris@0
|
470
|
Chris@0
|
471
|
Chris@0
|
472 /**
|
Chris@0
|
473 * Replace the entire contents of a token.
|
Chris@0
|
474 *
|
Chris@0
|
475 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
476 * @param string $content The new content of the token.
|
Chris@0
|
477 *
|
Chris@0
|
478 * @return bool If the change was accepted.
|
Chris@0
|
479 */
|
Chris@0
|
480 public function replaceToken($stackPtr, $content)
|
Chris@0
|
481 {
|
Chris@0
|
482 if ($this->_inConflict === true) {
|
Chris@0
|
483 return false;
|
Chris@0
|
484 }
|
Chris@0
|
485
|
Chris@0
|
486 if ($this->_inChangeset === false
|
Chris@0
|
487 && isset($this->_fixedTokens[$stackPtr]) === true
|
Chris@0
|
488 ) {
|
Chris@0
|
489 $indent = "\t";
|
Chris@0
|
490 if (empty($this->_changeset) === false) {
|
Chris@0
|
491 $indent .= "\t";
|
Chris@0
|
492 }
|
Chris@0
|
493
|
Chris@0
|
494 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
495 @ob_end_clean();
|
Chris@0
|
496 echo "$indent* token $stackPtr has already been modified, skipping *".PHP_EOL;
|
Chris@0
|
497 ob_start();
|
Chris@0
|
498 }
|
Chris@0
|
499
|
Chris@0
|
500 return false;
|
Chris@0
|
501 }
|
Chris@0
|
502
|
Chris@0
|
503 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
504 $bt = debug_backtrace();
|
Chris@0
|
505 if ($bt[1]['class'] === 'PHP_CodeSniffer_Fixer') {
|
Chris@0
|
506 $sniff = $bt[2]['class'];
|
Chris@0
|
507 $line = $bt[1]['line'];
|
Chris@0
|
508 } else {
|
Chris@0
|
509 $sniff = $bt[1]['class'];
|
Chris@0
|
510 $line = $bt[0]['line'];
|
Chris@0
|
511 }
|
Chris@0
|
512
|
Chris@0
|
513 $tokens = $this->_currentFile->getTokens();
|
Chris@0
|
514 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
515 $oldContent = PHP_CodeSniffer::prepareForOutput($this->_tokens[$stackPtr]);
|
Chris@0
|
516 $newContent = PHP_CodeSniffer::prepareForOutput($content);
|
Chris@0
|
517 if (trim($this->_tokens[$stackPtr]) === '' && isset($this->_tokens[($stackPtr + 1)]) === true) {
|
Chris@0
|
518 // Add some context for whitespace only changes.
|
Chris@0
|
519 $append = PHP_CodeSniffer::prepareForOutput($this->_tokens[($stackPtr + 1)]);
|
Chris@0
|
520 $oldContent .= $append;
|
Chris@0
|
521 $newContent .= $append;
|
Chris@0
|
522 }
|
Chris@0
|
523 }//end if
|
Chris@0
|
524
|
Chris@0
|
525 if ($this->_inChangeset === true) {
|
Chris@0
|
526 $this->_changeset[$stackPtr] = $content;
|
Chris@0
|
527
|
Chris@0
|
528 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
529 @ob_end_clean();
|
Chris@0
|
530 echo "\t\tQ: $sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
|
Chris@0
|
531 ob_start();
|
Chris@0
|
532 }
|
Chris@0
|
533
|
Chris@0
|
534 return true;
|
Chris@0
|
535 }
|
Chris@0
|
536
|
Chris@0
|
537 if (isset($this->_oldTokenValues[$stackPtr]) === false) {
|
Chris@0
|
538 $this->_oldTokenValues[$stackPtr] = array(
|
Chris@0
|
539 'curr' => $content,
|
Chris@0
|
540 'prev' => $this->_tokens[$stackPtr],
|
Chris@0
|
541 'loop' => $this->loops,
|
Chris@0
|
542 );
|
Chris@0
|
543 } else {
|
Chris@0
|
544 if ($this->_oldTokenValues[$stackPtr]['prev'] === $content
|
Chris@0
|
545 && $this->_oldTokenValues[$stackPtr]['loop'] === ($this->loops - 1)
|
Chris@0
|
546 ) {
|
Chris@0
|
547 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
548 $indent = "\t";
|
Chris@0
|
549 if (empty($this->_changeset) === false) {
|
Chris@0
|
550 $indent .= "\t";
|
Chris@0
|
551 }
|
Chris@0
|
552
|
Chris@0
|
553 $loop = $this->_oldTokenValues[$stackPtr]['loop'];
|
Chris@0
|
554
|
Chris@0
|
555 @ob_end_clean();
|
Chris@0
|
556 echo "$indent**** $sniff (line $line) has possible conflict with another sniff on loop $loop; caused by the following change ****".PHP_EOL;
|
Chris@0
|
557 echo "$indent**** replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\" ****".PHP_EOL;
|
Chris@0
|
558 }
|
Chris@0
|
559
|
Chris@0
|
560 if ($this->_oldTokenValues[$stackPtr]['loop'] >= ($this->loops - 1)) {
|
Chris@0
|
561 $this->_inConflict = true;
|
Chris@0
|
562 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
563 echo "$indent**** ignoring all changes until next loop ****".PHP_EOL;
|
Chris@0
|
564 }
|
Chris@0
|
565 }
|
Chris@0
|
566
|
Chris@0
|
567 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
568 ob_start();
|
Chris@0
|
569 }
|
Chris@0
|
570
|
Chris@0
|
571 return false;
|
Chris@0
|
572 }//end if
|
Chris@0
|
573
|
Chris@0
|
574 $this->_oldTokenValues[$stackPtr]['prev'] = $this->_oldTokenValues[$stackPtr]['curr'];
|
Chris@0
|
575 $this->_oldTokenValues[$stackPtr]['curr'] = $content;
|
Chris@0
|
576 $this->_oldTokenValues[$stackPtr]['loop'] = $this->loops;
|
Chris@0
|
577 }//end if
|
Chris@0
|
578
|
Chris@0
|
579 $this->_fixedTokens[$stackPtr] = $this->_tokens[$stackPtr];
|
Chris@0
|
580 $this->_tokens[$stackPtr] = $content;
|
Chris@0
|
581 $this->_numFixes++;
|
Chris@0
|
582
|
Chris@0
|
583 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
584 $indent = "\t";
|
Chris@0
|
585 if (empty($this->_changeset) === false) {
|
Chris@0
|
586 $indent .= "\tA: ";
|
Chris@0
|
587 }
|
Chris@0
|
588
|
Chris@0
|
589 @ob_end_clean();
|
Chris@0
|
590 echo "$indent$sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
|
Chris@0
|
591 ob_start();
|
Chris@0
|
592 }
|
Chris@0
|
593
|
Chris@0
|
594 return true;
|
Chris@0
|
595
|
Chris@0
|
596 }//end replaceToken()
|
Chris@0
|
597
|
Chris@0
|
598
|
Chris@0
|
599 /**
|
Chris@0
|
600 * Reverts the previous fix made to a token.
|
Chris@0
|
601 *
|
Chris@0
|
602 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
603 *
|
Chris@0
|
604 * @return bool If a change was reverted.
|
Chris@0
|
605 */
|
Chris@0
|
606 public function revertToken($stackPtr)
|
Chris@0
|
607 {
|
Chris@0
|
608 if (isset($this->_fixedTokens[$stackPtr]) === false) {
|
Chris@0
|
609 return false;
|
Chris@0
|
610 }
|
Chris@0
|
611
|
Chris@0
|
612 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
613 $bt = debug_backtrace();
|
Chris@0
|
614 if ($bt[1]['class'] === 'PHP_CodeSniffer_Fixer') {
|
Chris@0
|
615 $sniff = $bt[2]['class'];
|
Chris@0
|
616 $line = $bt[1]['line'];
|
Chris@0
|
617 } else {
|
Chris@0
|
618 $sniff = $bt[1]['class'];
|
Chris@0
|
619 $line = $bt[0]['line'];
|
Chris@0
|
620 }
|
Chris@0
|
621
|
Chris@0
|
622 $tokens = $this->_currentFile->getTokens();
|
Chris@0
|
623 $type = $tokens[$stackPtr]['type'];
|
Chris@0
|
624 $oldContent = PHP_CodeSniffer::prepareForOutput($this->_tokens[$stackPtr]);
|
Chris@0
|
625 $newContent = PHP_CodeSniffer::prepareForOutput($this->_fixedTokens[$stackPtr]);
|
Chris@0
|
626 if (trim($this->_tokens[$stackPtr]) === '' && isset($tokens[($stackPtr + 1)]) === true) {
|
Chris@0
|
627 // Add some context for whitespace only changes.
|
Chris@0
|
628 $append = PHP_CodeSniffer::prepareForOutput($this->_tokens[($stackPtr + 1)]);
|
Chris@0
|
629 $oldContent .= $append;
|
Chris@0
|
630 $newContent .= $append;
|
Chris@0
|
631 }
|
Chris@0
|
632 }//end if
|
Chris@0
|
633
|
Chris@0
|
634 $this->_tokens[$stackPtr] = $this->_fixedTokens[$stackPtr];
|
Chris@0
|
635 unset($this->_fixedTokens[$stackPtr]);
|
Chris@0
|
636 $this->_numFixes--;
|
Chris@0
|
637
|
Chris@0
|
638 if (PHP_CODESNIFFER_VERBOSITY > 1) {
|
Chris@0
|
639 $indent = "\t";
|
Chris@0
|
640 if (empty($this->_changeset) === false) {
|
Chris@0
|
641 $indent .= "\tR: ";
|
Chris@0
|
642 }
|
Chris@0
|
643
|
Chris@0
|
644 @ob_end_clean();
|
Chris@0
|
645 echo "$indent$sniff (line $line) reverted token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
|
Chris@0
|
646 ob_start();
|
Chris@0
|
647 }
|
Chris@0
|
648
|
Chris@0
|
649 return true;
|
Chris@0
|
650
|
Chris@0
|
651 }//end revertToken()
|
Chris@0
|
652
|
Chris@0
|
653
|
Chris@0
|
654 /**
|
Chris@0
|
655 * Replace the content of a token with a part of its current content.
|
Chris@0
|
656 *
|
Chris@0
|
657 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
658 * @param int $start The first character to keep.
|
Chris@0
|
659 * @param int $length The number of chacters to keep. If NULL, the content of
|
Chris@0
|
660 * the token from $start to the end of the content is kept.
|
Chris@0
|
661 *
|
Chris@0
|
662 * @return bool If the change was accepted.
|
Chris@0
|
663 */
|
Chris@0
|
664 public function substrToken($stackPtr, $start, $length=null)
|
Chris@0
|
665 {
|
Chris@0
|
666 $current = $this->getTokenContent($stackPtr);
|
Chris@0
|
667
|
Chris@0
|
668 if ($length === null) {
|
Chris@0
|
669 $newContent = substr($current, $start);
|
Chris@0
|
670 } else {
|
Chris@0
|
671 $newContent = substr($current, $start, $length);
|
Chris@0
|
672 }
|
Chris@0
|
673
|
Chris@0
|
674 return $this->replaceToken($stackPtr, $newContent);
|
Chris@0
|
675
|
Chris@0
|
676 }//end substrToken()
|
Chris@0
|
677
|
Chris@0
|
678
|
Chris@0
|
679 /**
|
Chris@0
|
680 * Adds a newline to end of a token's content.
|
Chris@0
|
681 *
|
Chris@0
|
682 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
683 *
|
Chris@0
|
684 * @return bool If the change was accepted.
|
Chris@0
|
685 */
|
Chris@0
|
686 public function addNewline($stackPtr)
|
Chris@0
|
687 {
|
Chris@0
|
688 $current = $this->getTokenContent($stackPtr);
|
Chris@0
|
689 return $this->replaceToken($stackPtr, $current.$this->_currentFile->eolChar);
|
Chris@0
|
690
|
Chris@0
|
691 }//end addNewline()
|
Chris@0
|
692
|
Chris@0
|
693
|
Chris@0
|
694 /**
|
Chris@0
|
695 * Adds a newline to the start of a token's content.
|
Chris@0
|
696 *
|
Chris@0
|
697 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
698 *
|
Chris@0
|
699 * @return bool If the change was accepted.
|
Chris@0
|
700 */
|
Chris@0
|
701 public function addNewlineBefore($stackPtr)
|
Chris@0
|
702 {
|
Chris@0
|
703 $current = $this->getTokenContent($stackPtr);
|
Chris@0
|
704 return $this->replaceToken($stackPtr, $this->_currentFile->eolChar.$current);
|
Chris@0
|
705
|
Chris@0
|
706 }//end addNewlineBefore()
|
Chris@0
|
707
|
Chris@0
|
708
|
Chris@0
|
709 /**
|
Chris@0
|
710 * Adds content to the end of a token's current content.
|
Chris@0
|
711 *
|
Chris@0
|
712 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
713 * @param string $content The content to add.
|
Chris@0
|
714 *
|
Chris@0
|
715 * @return bool If the change was accepted.
|
Chris@0
|
716 */
|
Chris@0
|
717 public function addContent($stackPtr, $content)
|
Chris@0
|
718 {
|
Chris@0
|
719 $current = $this->getTokenContent($stackPtr);
|
Chris@0
|
720 return $this->replaceToken($stackPtr, $current.$content);
|
Chris@0
|
721
|
Chris@0
|
722 }//end addContent()
|
Chris@0
|
723
|
Chris@0
|
724
|
Chris@0
|
725 /**
|
Chris@0
|
726 * Adds content to the start of a token's current content.
|
Chris@0
|
727 *
|
Chris@0
|
728 * @param int $stackPtr The position of the token in the token stack.
|
Chris@0
|
729 * @param string $content The content to add.
|
Chris@0
|
730 *
|
Chris@0
|
731 * @return bool If the change was accepted.
|
Chris@0
|
732 */
|
Chris@0
|
733 public function addContentBefore($stackPtr, $content)
|
Chris@0
|
734 {
|
Chris@0
|
735 $current = $this->getTokenContent($stackPtr);
|
Chris@0
|
736 return $this->replaceToken($stackPtr, $content.$current);
|
Chris@0
|
737
|
Chris@0
|
738 }//end addContentBefore()
|
Chris@0
|
739
|
Chris@0
|
740
|
Chris@0
|
741 }//end class
|