3131 * <rule ref="NetteCodingStandard.Namespaces.OptimizeGlobalCalls">
3232 * <properties>
3333 * <property name="optimizedFunctionsOnly" value="false"/>
34- * <property name="ignoredFunctions" type="array">
34+ * <property name="includedConstants" type="array">
35+ * <element value="PHP_*"/>
36+ * <element value="DIRECTORY_SEPARATOR"/>
37+ * </property>
38+ * <property name="excludedFunctions" type="array">
3539 * <element value="dump"/>
3640 * <element value="dd"/>
3741 * </property>
38- * <property name="ignoredConstants" type="array">
39- * <element value="SOME_CONSTANT"/>
40- * </property>
4142 * </properties>
4243 * </rule>
4344 * ```
5354class OptimizeGlobalCallsSniff implements Sniff
5455{
5556 public $ optimizedFunctionsOnly = true ;
56- public $ ignoredFunctions = [];
57- public $ ignoredConstants = [];
57+ public $ includedFunctions = [];
58+ public $ excludedFunctions = [];
59+ public $ includedConstants = [];
60+ public $ excludedConstants = [];
5861 private static $ processedFiles = [];
5962
6063 private $ compilerOptimizedFunctions = [
@@ -102,19 +105,26 @@ public function process(File $phpcsFile, $stackPtr)
102105 $ usedConstants = $ this ->findUsedGlobalConstants ($ phpcsFile , $ existingUseStatements );
103106
104107 $ finalFunctions = $ usedFunctions ;
105- if ($ this ->optimizedFunctionsOnly ) {
106- $ nonOptimizedToKeep = [];
108+ if (! empty ( $ this -> includedFunctions ) || $ this ->optimizedFunctionsOnly ) {
109+ $ nonIncludedToKeep = [];
107110 foreach ($ existingUseStatements ['all_functions ' ] as $ name ) {
108- if (!in_array (strtolower ($ name ), $ this ->compilerOptimizedFunctions , true )) {
109- if ($ this ->isFunctionUsedInCode ($ phpcsFile , $ name )) {
110- $ nonOptimizedToKeep [] = $ name ;
111- }
111+ if (!$ this ->isFunctionIncluded ($ name ) && $ this ->isFunctionUsedInCode ($ phpcsFile , $ name )) {
112+ $ nonIncludedToKeep [] = $ name ;
112113 }
113114 }
114- $ finalFunctions = array_values (array_unique (array_merge ($ finalFunctions , $ nonOptimizedToKeep )));
115+ $ finalFunctions = array_values (array_unique (array_merge ($ finalFunctions , $ nonIncludedToKeep )));
115116 }
116117
117118 $ finalConstants = $ usedConstants ;
119+ if (!empty ($ this ->includedConstants )) {
120+ $ nonIncludedToKeep = [];
121+ foreach ($ existingUseStatements ['all_constants ' ] as $ name ) {
122+ if (!$ this ->isConstantIncluded ($ name ) && $ this ->isConstantUsedInCode ($ phpcsFile , $ name )) {
123+ $ nonIncludedToKeep [] = $ name ;
124+ }
125+ }
126+ $ finalConstants = array_values (array_unique (array_merge ($ finalConstants , $ nonIncludedToKeep )));
127+ }
118128
119129 $ isCorrect = $ this ->isStateCorrect ($ phpcsFile , $ finalFunctions , $ finalConstants , $ existingUseStatements );
120130 $ hasBackslashesToRemove = $ this ->hasBackslashesToRemove ($ phpcsFile , $ finalFunctions , $ finalConstants );
@@ -293,6 +303,29 @@ private function isFunctionUsedInCode(File $phpcsFile, string $functionName): bo
293303 }
294304
295305
306+ private function isConstantUsedInCode (File $ phpcsFile , string $ constantName ): bool
307+ {
308+ $ tokens = $ phpcsFile ->getTokens ();
309+ for ($ i = 0 ; $ i < $ phpcsFile ->numTokens ; $ i ++) {
310+ if ($ tokens [$ i ]['code ' ] === T_STRING && $ tokens [$ i ]['content ' ] === $ constantName ) {
311+ $ nextToken = $ phpcsFile ->findNext (T_WHITESPACE , $ i + 1 , null , true );
312+ if ($ nextToken !== false && $ tokens [$ nextToken ]['code ' ] === T_OPEN_PARENTHESIS ) {
313+ continue ; // function call, not constant
314+ }
315+
316+ $ prevToken = $ phpcsFile ->findPrevious (T_WHITESPACE , $ i - 1 , null , true );
317+ if (
318+ $ prevToken === false
319+ || !in_array ($ tokens [$ prevToken ]['code ' ], [T_OBJECT_OPERATOR , T_DOUBLE_COLON , T_NULLSAFE_OBJECT_OPERATOR , T_COLON ], true )
320+ ) {
321+ return true ;
322+ }
323+ }
324+ }
325+ return false ;
326+ }
327+
328+
296329 private function isFunctionDeclaration (File $ phpcsFile , int $ stackPtr ): bool
297330 {
298331 $ prevSemicolon = $ phpcsFile ->findPrevious (T_SEMICOLON , $ stackPtr - 1 );
@@ -322,7 +355,6 @@ private function findUsedGlobalFunctions(File $phpcsFile, array $existingUseStat
322355 {
323356 $ tokens = $ phpcsFile ->getTokens ();
324357 $ usedFunctions = [];
325- $ ignoredFunctions = array_map ('strtolower ' , $ this ->ignoredFunctions );
326358
327359 for ($ i = 0 ; $ i < $ phpcsFile ->numTokens ; $ i ++) {
328360 if ($ this ->isWithinUseStatement ($ i , $ existingUseStatements )) {
@@ -342,20 +374,9 @@ private function findUsedGlobalFunctions(File $phpcsFile, array $existingUseStat
342374 continue ;
343375 }
344376
345- if (in_array (strtolower ($ functionName ), $ ignoredFunctions , true )) {
346- continue ;
347- }
348-
349- if ($ this ->optimizedFunctionsOnly ) {
350- if (!in_array (strtolower ($ functionName ), $ this ->compilerOptimizedFunctions , true )) {
351- continue ;
352- }
353- } else {
354- if (!function_exists ($ functionName )) {
355- continue ;
356- }
377+ if ($ this ->isFunctionIncluded ($ functionName )) {
378+ $ usedFunctions [] = $ functionName ;
357379 }
358- $ usedFunctions [] = $ functionName ;
359380 continue ;
360381 }
361382
@@ -369,9 +390,6 @@ private function findUsedGlobalFunctions(File $phpcsFile, array $existingUseStat
369390 }
370391
371392 $ functionName = $ tokens [$ i ]['content ' ];
372- if (in_array (strtolower ($ functionName ), $ ignoredFunctions , true )) {
373- continue ;
374- }
375393
376394 $ nextToken = $ phpcsFile ->findNext (T_WHITESPACE , $ i + 1 , null , true );
377395 if ($ nextToken === false || $ tokens [$ nextToken ]['code ' ] !== T_OPEN_PARENTHESIS ) {
@@ -393,16 +411,9 @@ private function findUsedGlobalFunctions(File $phpcsFile, array $existingUseStat
393411 }
394412 }
395413
396- if ($ this ->optimizedFunctionsOnly ) {
397- if (!in_array (strtolower ($ functionName ), $ this ->compilerOptimizedFunctions , true )) {
398- continue ;
399- }
400- } else {
401- if (!function_exists ($ functionName )) {
402- continue ;
403- }
414+ if ($ this ->isFunctionIncluded ($ functionName )) {
415+ $ usedFunctions [] = $ functionName ;
404416 }
405- $ usedFunctions [] = $ functionName ;
406417 }
407418 return array_values (array_unique ($ usedFunctions ));
408419 }
@@ -431,14 +442,9 @@ private function findUsedGlobalConstants(File $phpcsFile, array $existingUseStat
431442 continue ; // This is a function call, not a constant
432443 }
433444
434- if ($ this ->isIgnoredConstant ($ constantName )) {
435- continue ;
445+ if ($ this ->isConstantIncluded ($ constantName )) {
446+ $ usedConstants [] = $ constantName ;
436447 }
437-
438- if (!defined ($ constantName )) {
439- continue ;
440- }
441- $ usedConstants [] = $ constantName ;
442448 continue ;
443449 }
444450
@@ -447,9 +453,6 @@ private function findUsedGlobalConstants(File $phpcsFile, array $existingUseStat
447453 }
448454
449455 $ constantName = $ tokens [$ i ]['content ' ];
450- if ($ this ->isIgnoredConstant ($ constantName )) {
451- continue ;
452- }
453456
454457 $ prevTokenPtr = $ phpcsFile ->findPrevious (T_WHITESPACE , $ i - 1 , null , true );
455458 $ nextTokenPtr = $ phpcsFile ->findNext (T_WHITESPACE , $ i + 1 , null , true );
@@ -482,10 +485,9 @@ private function findUsedGlobalConstants(File $phpcsFile, array $existingUseStat
482485 continue ;
483486 }
484487
485- if (! defined ($ constantName )) {
486- continue ;
488+ if ($ this -> isConstantIncluded ($ constantName )) {
489+ $ usedConstants [] = $ constantName ;
487490 }
488- $ usedConstants [] = $ constantName ;
489491 }
490492 return array_values (array_unique ($ usedConstants ));
491493 }
@@ -681,12 +683,12 @@ private function hasNamespace(File $phpcsFile): bool
681683 }
682684
683685
684- private function isIgnoredConstant (string $ constantName ): bool
686+ private function matchesPatternList (string $ name , array $ patterns , bool $ caseSensitive = false ): bool
685687 {
686- $ ignored = array_merge ($ this ->builtInIgnoredConstants , $ this ->ignoredConstants );
687- foreach ($ ignored as $ pattern ) {
688+ foreach ($ patterns as $ pattern ) {
688689 $ regex = str_replace ('\* ' , '.* ' , preg_quote ($ pattern , '/ ' ));
689- if (preg_match ('/^ ' . $ regex . '$/i ' , $ constantName )) {
690+ $ flags = $ caseSensitive ? '' : 'i ' ;
691+ if (preg_match ('/^ ' . $ regex . '$/ ' . $ flags , $ name )) {
690692 return true ;
691693 }
692694 }
@@ -695,6 +697,45 @@ private function isIgnoredConstant(string $constantName): bool
695697 }
696698
697699
700+ private function isExcludedConstant (string $ constantName ): bool
701+ {
702+ $ excluded = array_merge ($ this ->builtInIgnoredConstants , $ this ->excludedConstants );
703+ return $ this ->matchesPatternList ($ constantName , $ excluded );
704+ }
705+
706+
707+ private function isFunctionIncluded (string $ functionName ): bool
708+ {
709+ if ($ this ->matchesPatternList ($ functionName , $ this ->excludedFunctions )) {
710+ return false ;
711+ }
712+
713+ if (!empty ($ this ->includedFunctions )) {
714+ return $ this ->matchesPatternList ($ functionName , $ this ->includedFunctions );
715+ }
716+
717+ if ($ this ->optimizedFunctionsOnly ) {
718+ return in_array (strtolower ($ functionName ), $ this ->compilerOptimizedFunctions , true );
719+ }
720+
721+ return function_exists ($ functionName );
722+ }
723+
724+
725+ private function isConstantIncluded (string $ constantName ): bool
726+ {
727+ if ($ this ->isExcludedConstant ($ constantName )) {
728+ return false ;
729+ }
730+
731+ if (!empty ($ this ->includedConstants )) {
732+ return $ this ->matchesPatternList ($ constantName , $ this ->includedConstants );
733+ }
734+
735+ return defined ($ constantName );
736+ }
737+
738+
698739 private function createLookupArrays (array $ usedFunctions , array $ usedConstants ): array
699740 {
700741 $ functionLookup = [];
0 commit comments