| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | namespace PHPSA\Analyzer\Pass\Expression\FunctionCall; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | use PHPSA\Context; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | use PhpParser\Node; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | use PhpParser\Node\Stmt; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | use PHPSA\Analyzer\Pass; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | use PhpParser\Node\Expr\Array_; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | use PhpParser\Node\Expr\FuncCall; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | use PhpParser\Node\Scalar\String_; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | use PHPSA\Analyzer\Helper\DefaultMetadataPassTrait; | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 13 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 14 |  |  | class FunctionStringFormater extends AbstractFunctionCallAnalyzer | 
            
                                                                        
                            
            
                                    
            
            
                | 15 |  |  | { | 
            
                                                                        
                            
            
                                    
            
            
                | 16 |  |  |     use DefaultMetadataPassTrait; | 
            
                                                                        
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 18 |  |  |     const DESCRIPTION = 'Format string has same number of placeholders as parameters are passed into and forbid invalid type formats.'; | 
            
                                                                        
                            
            
                                    
            
            
                | 19 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 20 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 21 |  |  |      * @var array functions | 
            
                                                                        
                            
            
                                    
            
            
                | 22 |  |  |      */ | 
            
                                                                        
                            
            
                                    
            
            
                | 23 |  |  |     protected static $functions = [ | 
            
                                                                        
                            
            
                                    
            
            
                | 24 |  |  |         'printf' => 'printf', | 
            
                                                                        
                            
            
                                    
            
            
                | 25 |  |  |         'sprintf' => 'sprintf', | 
            
                                                                        
                            
            
                                    
            
            
                | 26 |  |  |         'vprintf' => 'vprintf', | 
            
                                                                        
                            
            
                                    
            
            
                | 27 |  |  |         'vsprintf' => 'vsprintf' | 
            
                                                                        
                            
            
                                    
            
            
                | 28 |  |  |     ]; | 
            
                                                                        
                            
            
                                    
            
            
                | 29 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 30 |  |  |      * Placeholders for type format | 
            
                                                                        
                            
            
                                    
            
            
                | 31 |  |  |      * @var array | 
            
                                                                        
                            
            
                                    
            
            
                | 32 |  |  |      */ | 
            
                                                                        
                            
            
                                    
            
            
                | 33 |  |  |     protected $placeholders = []; | 
            
                                                                        
                            
            
                                    
            
            
                | 34 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 35 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 36 |  |  |      * @param FuncCall $funcCall | 
            
                                                                        
                            
            
                                    
            
            
                | 37 |  |  |      * @param Context $context | 
            
                                                                        
                            
            
                                    
            
            
                | 38 |  |  |      * @return bool | 
            
                                                                        
                            
            
                                    
            
            
                | 39 |  |  |      */ | 
            
                                                                        
                            
            
                                    
            
            
                | 40 | 7 |  |     public function pass(FuncCall $funcCall, Context $context) | 
            
                                                                        
                            
            
                                    
            
            
                | 41 |  |  |     { | 
            
                                                                        
                            
            
                                    
            
            
                | 42 | 7 |  |         $functionName = $this->resolveFunctionName($funcCall, $context); | 
            
                                                                        
                            
            
                                    
            
            
                | 43 | 7 |  |         if ($functionName && isset(self::$functions[$functionName])) { | 
            
                                                                        
                            
            
                                    
            
            
                | 44 | 1 |  |             if ($funcCall->args) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                        
                            
            
                                    
            
            
                | 45 | 1 |  |                 $args = $funcCall->args; | 
            
                                                                        
                            
            
                                    
            
            
                | 46 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 47 | 1 |  |                 if (! ($args[0]->value instanceof String_)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 48 | 1 |  |                     $context->notice( | 
            
                                                                        
                            
            
                                    
            
            
                | 49 | 1 |  |                         'function_argument_invalid', | 
            
                                                                        
                            
            
                                    
            
            
                | 50 | 1 |  |                         sprintf('First parameter of %s must be string', $functionName), | 
            
                                                                        
                            
            
                                    
            
            
                | 51 |  |  |                         $funcCall | 
            
                                                                        
                            
            
                                    
            
            
                | 52 | 1 |  |                     ); | 
            
                                                                        
                            
            
                                    
            
            
                | 53 | 1 |  |                 } | 
            
                                                                        
                            
            
                                    
            
            
                | 54 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 55 | 1 |  |                 if (($args[0]->value instanceof String_)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 56 | 1 |  |                     $string = $args[0]->value->value; | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                        
                            
            
                                    
            
            
                | 57 |  |  |                     // get invalid placeholders | 
            
                                                                        
                            
            
                                    
            
            
                | 58 | 1 |  |                     preg_match_all("/(?<!\x25)\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?[^bcdeEufFgGosxX(%)]/", $string, $this->placeholders); | 
            
                                                                        
                            
            
                                    
            
            
                | 59 | 1 |  |                     if (count($this->placeholders[0]) > 0) { | 
            
                                                                        
                            
            
                                    
            
            
                | 60 | 1 |  |                         $context->notice( | 
            
                                                                        
                            
            
                                    
            
            
                | 61 | 1 |  |                             'function_format_type_invalid', | 
            
                                                                        
                            
            
                                    
            
            
                | 62 | 1 |  |                             sprintf('Unexpected type format in %s function string', $functionName), | 
            
                                                                        
                            
            
                                    
            
            
                | 63 |  |  |                             $funcCall | 
            
                                                                        
                            
            
                                    
            
            
                | 64 | 1 |  |                         ); | 
            
                                                                        
                            
            
                                    
            
            
                | 65 | 1 |  |                     } else { | 
            
                                                                        
                            
            
                                    
            
            
                | 66 |  |  |                         // get valid placesholders | 
            
                                                                        
                            
            
                                    
            
            
                | 67 | 1 |  |                         preg_match_all("/(?<!\x25)\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([bcdeEufFgGosxX])/", $string, $this->placeholders); | 
            
                                                                        
                            
            
                                    
            
            
                | 68 | 1 |  |                         if ($args[1]->value instanceof Array_) { | 
            
                                                                        
                            
            
                                    
            
            
                | 69 | 1 |  |                             if (count($this->placeholders[0]) !== count($args[1]->value->items)) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                        
                            
            
                                    
            
            
                | 70 | 1 |  |                                 $context->notice( | 
            
                                                                        
                            
            
                                    
            
            
                | 71 | 1 |  |                                     'function_array_length_invalid', | 
            
                                                                        
                            
            
                                    
            
            
                | 72 | 1 |  |                                     sprintf('Unexpected length of array passed to %s', $functionName), | 
            
                                                                        
                            
            
                                    
            
            
                | 73 |  |  |                                     $funcCall | 
            
                                                                        
                            
            
                                    
            
            
                | 74 | 1 |  |                                 ); | 
            
                                                                        
                            
            
                                    
            
            
                | 75 | 1 |  |                             } | 
            
                                                                        
                            
            
                                    
            
            
                | 76 | 1 |  |                         } else { | 
            
                                                                        
                            
            
                                    
            
            
                | 77 | 1 |  |                             if (count($this->placeholders[0]) !== (count($args) - 1)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 78 | 1 |  |                                 $context->notice( | 
            
                                                                        
                            
            
                                    
            
            
                | 79 | 1 |  |                                     'function_arguments_length_invalid', | 
            
                                                                        
                            
            
                                    
            
            
                | 80 | 1 |  |                                     sprintf('Unexpected length of arguments passed to %s', $functionName), | 
            
                                                                        
                            
            
                                    
            
            
                | 81 |  |  |                                     $funcCall | 
            
                                                                        
                            
            
                                    
            
            
                | 82 | 1 |  |                                 ); | 
            
                                                                        
                            
            
                                    
            
            
                | 83 | 1 |  |                             } | 
            
                                                                        
                            
            
                                    
            
            
                | 84 |  |  |                         } | 
            
                                                                        
                            
            
                                    
            
            
                | 85 |  |  |                     } | 
            
                                                                        
                            
            
                                    
            
            
                | 86 | 1 |  |                 } | 
            
                                                                        
                            
            
                                    
            
            
                | 87 | 1 |  |             } | 
            
                                                                        
                            
            
                                    
            
            
                | 88 | 1 |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 89 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 90 | 7 |  |         return true; | 
            
                                                                        
                            
            
                                    
            
            
                | 91 |  |  |     } | 
            
                                                                        
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 93 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 94 |  |  |      * @return array | 
            
                                                                        
                            
            
                                    
            
            
                | 95 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 | 2 |  |     public function getRegister() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |         return [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |             FuncCall::class | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 | 2 |  |         ]; | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 101 |  |  |     } | 
            
                                                                        
                                                                
            
                                    
            
            
                | 102 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 103 |  |  |  | 
            
                        
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.