Skip to content

Commit af6dcc1

Browse files
committed
PEAR.Functions.FunctionCallSignature now adjusts the indent of function argument contents during auto-fixing (ref #2635)
1 parent 9604fe2 commit af6dcc1

9 files changed

+290
-8
lines changed

package.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
3636
-- The cache is now invalidated for a file when its permissions are changed
3737
- File::getMethodParameters() now supports arrow functions
3838
- File::getMethodProperties() now supports arrow functions
39+
- Added Fixer::changeCodeBlockIndent() to change the indent of a code block while auto-fixing
40+
-- Can be used to either increase or decrease the indent
41+
-- Useful when moving the start position of something like a closure, where you want the content to also move
3942
- Added Generic.Files.ExecutableFile sniff
4043
-- Ensures that files are not executable
4144
-- Thanks to Matthew Peveler for the contribution
4245
- Generic.CodeAnalysis.EmptyPhpStatement now reports unnecessary semicolons after control structure closing braces
4346
-- Thanks to Vincent Langlet for the patch
4447
- Generic.WhiteSpace.ScopeIndent now supports static arrow functions
48+
- PEAR.Functions.FunctionCallSignature now adjusts the indent of function argument contents during auto-fixing
49+
-- Previously, only the first line of an argument was changed, leading to inconsistent indents
50+
-- This change also applies to PSR2.Methods.FunctionCallSignature
4551
- PSR2.ControlStructures.ControlStructureSpacing now checks whitespace before the closing parenthesis of multi-line control structures
4652
-- Previously, it incorrectly applied the whitespace check for single-line definitions only
4753
- PSR12.Traits.UseDeclaration now ensures all trait import statements are grouped together

src/Fixer.php

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,14 @@ public function beginChangeset()
351351
}
352352

353353
if (PHP_CODESNIFFER_VERBOSITY > 1) {
354-
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
355-
$sniff = $bt[1]['class'];
356-
$line = $bt[0]['line'];
354+
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
355+
if ($bt[1]['class'] === __CLASS__) {
356+
$sniff = 'Fixer';
357+
} else {
358+
$sniff = Util\Common::getSniffCode($bt[1]['class']);
359+
}
357360

358-
$sniff = Util\Common::getSniffCode($sniff);
361+
$line = $bt[0]['line'];
359362

360363
@ob_end_clean();
361364
echo "\t=> Changeset started by $sniff:$line".PHP_EOL;
@@ -730,4 +733,66 @@ public function addContentBefore($stackPtr, $content)
730733
}//end addContentBefore()
731734

732735

736+
/**
737+
* Adjust the indent of a code block.
738+
*
739+
* @param int $start The position of the token in the token stack
740+
* to start adjusting the indent from.
741+
* @param int $end The position of the token in the token stack
742+
* to end adjusting the indent.
743+
* @param int $change The number of spaces to adjust the indent by
744+
* (positive or negative).
745+
*
746+
* @return bool If the change was accepted.
747+
*/
748+
public function changeCodeBlockIndent($start, $end, $change)
749+
{
750+
$tokens = $this->currentFile->getTokens();
751+
752+
$baseIndent = '';
753+
if ($change > 0) {
754+
$baseIndent = str_repeat(' ', $change);
755+
}
756+
757+
$useChangeset = false;
758+
if ($this->inChangeset === false) {
759+
$this->beginChangeset();
760+
$useChangeset = true;
761+
}
762+
763+
for ($i = $start; $i <= $end; $i++) {
764+
if ($tokens[$i]['column'] !== 1
765+
|| $tokens[($i + 1)]['line'] !== $tokens[$i]['line']
766+
) {
767+
continue;
768+
}
769+
770+
$length = 0;
771+
if ($tokens[$i]['code'] === T_WHITESPACE
772+
|| $tokens[$i]['code'] === T_DOC_COMMENT_WHITESPACE
773+
) {
774+
$length = $tokens[$i]['length'];
775+
776+
$padding = ($length + $change);
777+
if ($padding > 0) {
778+
$padding = str_repeat(' ', $padding);
779+
} else {
780+
$padding = '';
781+
}
782+
783+
$newContent = $padding.ltrim($tokens[$i]['content']);
784+
} else {
785+
$newContent = $baseIndent.$tokens[$i]['content'];
786+
}
787+
788+
$this->replaceToken($i, $newContent);
789+
}//end for
790+
791+
if ($useChangeset === true) {
792+
$this->endChangeset();
793+
}
794+
795+
}//end changeCodeBlockIndent()
796+
797+
733798
}//end class

src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
449449
}
450450
}//end if
451451

452+
$i = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
453+
452454
if ($tokens[($i - 1)]['code'] === T_WHITESPACE
453455
&& $tokens[($i - 1)]['line'] === $tokens[$i]['line']
454456
) {
@@ -548,18 +550,29 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
548550

549551
$fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data);
550552
if ($fix === true) {
553+
$phpcsFile->fixer->beginChangeset();
554+
551555
$padding = str_repeat(' ', $expectedIndent);
552556
if ($foundIndent === 0) {
553557
$phpcsFile->fixer->addContentBefore($i, $padding);
558+
if (isset($tokens[$i]['scope_opener']) === true) {
559+
$phpcsFile->fixer->changeCodeBlockIndent($i, $tokens[$i]['scope_closer'], $expectedIndent);
560+
}
554561
} else {
555562
if ($tokens[$i]['code'] === T_COMMENT) {
556563
$comment = $padding.ltrim($tokens[$i]['content']);
557564
$phpcsFile->fixer->replaceToken($i, $comment);
558565
} else {
559566
$phpcsFile->fixer->replaceToken($i, $padding);
560567
}
568+
569+
if (isset($tokens[($i + 1)]['scope_opener']) === true) {
570+
$phpcsFile->fixer->changeCodeBlockIndent(($i + 1), $tokens[($i + 1)]['scope_closer'], ($expectedIndent - $foundIndent));
571+
}
561572
}
562-
}
573+
574+
$phpcsFile->fixer->endChangeset();
575+
}//end if
563576
}//end if
564577
} else {
565578
$nextCode = $i;
@@ -611,4 +624,91 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
611624
}//end processMultiLineCall()
612625

613626

627+
/**
628+
* Processes this test, when one of its tokens is encountered.
629+
*
630+
* @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
631+
* @param int $stackPtr The position of the current token
632+
* in the stack passed in $tokens.
633+
* @param int $length The length of the new indent.
634+
* @param int $change The difference in length between
635+
* the old and new indent.
636+
*
637+
* @return bool
638+
*/
639+
protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
640+
{
641+
$tokens = $phpcsFile->getTokens();
642+
643+
// We don't adjust indents outside of PHP.
644+
if ($tokens[$stackPtr]['code'] === T_INLINE_HTML) {
645+
return false;
646+
}
647+
648+
$padding = '';
649+
if ($length > 0) {
650+
if ($this->tabIndent === true) {
651+
$numTabs = floor($length / $this->tabWidth);
652+
if ($numTabs > 0) {
653+
$numSpaces = ($length - ($numTabs * $this->tabWidth));
654+
$padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
655+
}
656+
} else {
657+
$padding = str_repeat(' ', $length);
658+
}
659+
}
660+
661+
if ($tokens[$stackPtr]['column'] === 1) {
662+
$trimmed = ltrim($tokens[$stackPtr]['content']);
663+
$accepted = $phpcsFile->fixer->replaceToken($stackPtr, $padding.$trimmed);
664+
} else {
665+
// Easier to just replace the entire indent.
666+
$accepted = $phpcsFile->fixer->replaceToken(($stackPtr - 1), $padding);
667+
}
668+
669+
if ($accepted === false) {
670+
return false;
671+
}
672+
673+
if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
674+
// We adjusted the start of a comment, so adjust the rest of it
675+
// as well so the alignment remains correct.
676+
for ($x = ($stackPtr + 1); $x < $tokens[$stackPtr]['comment_closer']; $x++) {
677+
if ($tokens[$x]['column'] !== 1) {
678+
continue;
679+
}
680+
681+
$length = 0;
682+
if ($tokens[$x]['code'] === T_DOC_COMMENT_WHITESPACE) {
683+
$length = $tokens[$x]['length'];
684+
}
685+
686+
$padding = ($length + $change);
687+
if ($padding > 0) {
688+
if ($this->tabIndent === true) {
689+
$numTabs = floor($padding / $this->tabWidth);
690+
$numSpaces = ($padding - ($numTabs * $this->tabWidth));
691+
$padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
692+
} else {
693+
$padding = str_repeat(' ', $padding);
694+
}
695+
} else {
696+
$padding = '';
697+
}
698+
699+
$phpcsFile->fixer->replaceToken($x, $padding);
700+
if ($this->debug === true) {
701+
$length = strlen($padding);
702+
$line = $tokens[$x]['line'];
703+
$type = $tokens[$x]['type'];
704+
echo "\t=> Indent adjusted to $length for $type on line $line".PHP_EOL;
705+
}
706+
}//end for
707+
}//end if
708+
709+
return true;
710+
711+
}//end adjustIndent()
712+
713+
614714
}//end class

src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,19 @@ $salesOrderThresholdTransfer->fromArray($salesOrderThresholdEntity->toArray(), t
506506
)->setStore(
507507
(new StoreTransfer())->fromArray($salesOrderThresholdEntity->getStore()->toArray(), true)
508508
);
509+
510+
return trim(preg_replace_callback(
511+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
512+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
513+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
514+
function (array $term) use ($mode): string {
515+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
516+
if (mb_strlen($term[1], 'UTF-8') > 1) {
517+
// in boolean search mode '' (empty) means OR, '-' means NOT
518+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
519+
}
520+
521+
return '';
522+
},
523+
$search
524+
));

src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc.fixed

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ array_map(
8282

8383
array_map(
8484
function($x)
85-
{
86-
return trim($x);
87-
},
85+
{
86+
return trim($x);
87+
},
8888
$array
8989
);
9090

@@ -516,3 +516,21 @@ $salesOrderThresholdTransfer->fromArray($salesOrderThresholdEntity->toArray(), t
516516
)->setStore(
517517
(new StoreTransfer())->fromArray($salesOrderThresholdEntity->getStore()->toArray(), true)
518518
);
519+
520+
return trim(
521+
preg_replace_callback(
522+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
523+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
524+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
525+
function (array $term) use ($mode): string {
526+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
527+
if (mb_strlen($term[1], 'UTF-8') > 1) {
528+
// in boolean search mode '' (empty) means OR, '-' means NOT
529+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
530+
}
531+
532+
return '';
533+
},
534+
$search
535+
)
536+
);

src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ public function getErrorList($testFile='FunctionCallSignatureUnitTest.inc')
120120
441 => 1,
121121
442 => 1,
122122
464 => 1,
123+
510 => 1,
124+
513 => 1,
125+
514 => 1,
126+
523 => 1,
127+
524 => 3,
123128
];
124129

125130
}//end getErrorList()

src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,35 @@ $obj->{$x}(1,
210210
'a','b'
211211
)('c',
212212
'd');
213+
214+
return trim(preg_replace_callback(
215+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
216+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
217+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
218+
function (array $term) use ($mode): string {
219+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
220+
if (mb_strlen($term[1], 'UTF-8') > 1) {
221+
// in boolean search mode '' (empty) means OR, '-' means NOT
222+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
223+
}
224+
225+
return '';
226+
},
227+
$search
228+
));
229+
230+
return trim(preg_replace_callback(
231+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
232+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
233+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
234+
function (array $term) use ($mode): string {
235+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
236+
if (mb_strlen($term[1], 'UTF-8') > 1) {
237+
// in boolean search mode '' (empty) means OR, '-' means NOT
238+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
239+
}
240+
241+
return '';
242+
},
243+
$search
244+
));

src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc.fixed

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,35 @@ $obj->{$x}(
223223
'c',
224224
'd'
225225
);
226+
227+
return trim(preg_replace_callback(
228+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
229+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
230+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
231+
function (array $term) use ($mode): string {
232+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
233+
if (mb_strlen($term[1], 'UTF-8') > 1) {
234+
// in boolean search mode '' (empty) means OR, '-' means NOT
235+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
236+
}
237+
238+
return '';
239+
},
240+
$search
241+
));
242+
243+
return trim(preg_replace_callback(
244+
// sprintf replaces IGNORED_CHARS multiple times: for %s as well as %1$s (argument numbering)
245+
// /[%s]*([^%1$s]+)/ results in /[IGNORED_CHARS]*([^IGNORED_CHARS]+)/
246+
sprintf('/[%s]*([^%1$s]+)/', self::IGNORED_CHARS),
247+
function (array $term) use ($mode): string {
248+
// query pieces have to bigger than one char, otherwise they are too expensive for the search
249+
if (mb_strlen($term[1], 'UTF-8') > 1) {
250+
// in boolean search mode '' (empty) means OR, '-' means NOT
251+
return sprintf('%s%s ', $mode === 'AND' ? '+' : '', self::extractUmlauts($term[1]));
252+
}
253+
254+
return '';
255+
},
256+
$search
257+
));

src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ public function getErrorList()
5959
210 => 2,
6060
211 => 1,
6161
212 => 2,
62+
217 => 1,
63+
218 => 1,
64+
227 => 1,
65+
228 => 1,
66+
233 => 1,
67+
234 => 1,
68+
242 => 1,
69+
243 => 1,
6270
];
6371

6472
}//end getErrorList()

0 commit comments

Comments
 (0)