@@ -59,7 +59,13 @@ class VariableAnalysisSniff implements Sniff {
5959 * ignore from undefined variable warnings. For example, to ignore the variables
6060 * `$post` and `$undefined`, this could be set to `'post undefined'`.
6161 */
62- public $ validUdefinedVariableNames = null ;
62+ public $ validUndefinedVariableNames = null ;
63+
64+ /**
65+ * Allows unused arguments in a function definition if they are
66+ * followed by an argument which is used.
67+ */
68+ public $ allowUnusedParametersBeforeUsed = false ;
6369
6470 public function register () {
6571 return [
@@ -153,6 +159,23 @@ protected function getOrCreateVariableInfo($varName, $currScope) {
153159 return $ scopeInfo ->variables [$ varName ];
154160 }
155161
162+ protected function areFollowingArgumentsUsed ($ varInfo , $ scopeInfo ) {
163+ $ foundVarPosition = false ;
164+ foreach ($ scopeInfo ->variables as $ variable ) {
165+ if ($ variable === $ varInfo ) {
166+ $ foundVarPosition = true ;
167+ continue ;
168+ }
169+ if (! $ foundVarPosition ) {
170+ continue ;
171+ }
172+ if ($ variable ->firstRead ) {
173+ return true ;
174+ }
175+ }
176+ return false ;
177+ }
178+
156179 protected function markVariableAssignment ($ varName , $ stackPtr , $ currScope ) {
157180 $ varInfo = $ this ->getOrCreateVariableInfo ($ varName , $ currScope );
158181 if (!isset ($ varInfo ->scopeType )) {
@@ -223,7 +246,6 @@ protected function isVariableUndefined($varName, $stackPtr, $currScope) {
223246 return false ;
224247 }
225248 if (isset ($ varInfo ->firstDeclared ) && $ varInfo ->firstDeclared <= $ stackPtr ) {
226- // TODO: do we want to check scopeType here?
227249 return false ;
228250 }
229251 if (isset ($ varInfo ->firstInitialized ) && $ varInfo ->firstInitialized <= $ stackPtr ) {
@@ -261,7 +283,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
261283 if (($ functionPtr !== false )
262284 && (($ tokens [$ functionPtr ]['code ' ] === T_FUNCTION )
263285 || ($ tokens [$ functionPtr ]['code ' ] === T_CLOSURE ))) {
264- // TODO: typeHint
265286 $ this ->markVariableDeclaration ($ varName , 'param ' , null , $ stackPtr , $ functionPtr );
266287 // Are we pass-by-reference?
267288 $ referencePtr = $ phpcsFile ->findPrevious (T_WHITESPACE , $ stackPtr - 1 , null , true , null , true );
@@ -287,7 +308,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
287308 // $functionPtr is at the use, we need the function keyword for start of scope.
288309 $ functionPtr = $ phpcsFile ->findPrevious (T_CLOSURE , $ functionPtr - 1 , $ currScope + 1 , false , null , true );
289310 if ($ functionPtr !== false ) {
290- // TODO: typeHints in use?
291311 $ this ->markVariableDeclaration ($ varName , 'bound ' , null , $ stackPtr , $ functionPtr );
292312 $ this ->markVariableAssignment ($ varName , $ stackPtr , $ functionPtr );
293313
@@ -317,7 +337,6 @@ protected function checkForCatchBlock(File $phpcsFile, $stackPtr, $varName, $cur
317337 $ catchPtr = $ phpcsFile ->findPrevious (T_WHITESPACE , $ openPtr - 1 , null , true , null , true );
318338 if (($ catchPtr !== false ) && ($ tokens [$ catchPtr ]['code ' ] === T_CATCH )) {
319339 // Scope of the exception var is actually the function, not just the catch block.
320- // TODO: typeHint
321340 $ this ->markVariableDeclaration ($ varName , 'local ' , null , $ stackPtr , $ currScope , true );
322341 $ this ->markVariableAssignment ($ varName , $ stackPtr , $ currScope );
323342 if ($ this ->allowUnusedCaughtExceptions ) {
@@ -405,7 +424,6 @@ protected function checkForStaticMember(File $phpcsFile, $stackPtr, $varName, $c
405424
406425 protected function checkForStaticOutsideClass (File $ phpcsFile , $ stackPtr , $ varName , $ currScope ) {
407426 // Are we refering to self:: outside a class?
408- // TODO: not sure this is our business or should be some other sniff.
409427
410428 $ tokens = $ phpcsFile ->getTokens ();
411429 $ token = $ tokens [$ stackPtr ];
@@ -925,17 +943,20 @@ protected function processScopeClose(File $phpcsFile, $stackPtr) {
925943 return ;
926944 }
927945 foreach ($ scopeInfo ->variables as $ varInfo ) {
928- $ this ->processScopeCloseForVariable ($ phpcsFile , $ varInfo );
946+ $ this ->processScopeCloseForVariable ($ phpcsFile , $ varInfo, $ scopeInfo );
929947 }
930948 }
931949
932- protected function processScopeCloseForVariable ($ phpcsFile , $ varInfo ) {
950+ protected function processScopeCloseForVariable ($ phpcsFile , $ varInfo, $ scopeInfo ) {
933951 if ($ varInfo ->ignoreUnused || isset ($ varInfo ->firstRead )) {
934952 return ;
935953 }
936954 if ($ this ->allowUnusedFunctionParameters && $ varInfo ->scopeType === 'param ' ) {
937955 return ;
938956 }
957+ if ($ this ->allowUnusedParametersBeforeUsed && $ varInfo ->scopeType === 'param ' && $ this ->areFollowingArgumentsUsed ($ varInfo , $ scopeInfo )) {
958+ return ;
959+ }
939960 if ($ varInfo ->passByReference && isset ($ varInfo ->firstInitialized )) {
940961 // If we're pass-by-reference then it's a common pattern to
941962 // use the variable to return data to the caller, so any
0 commit comments