@@ -58,7 +58,7 @@ public function __construct(private ReflectionProvider $reflectionProvider, priv
5858
5959 public function getOffsetValueType (Type $ inputType , Type $ offsetType , ?Type $ filterType , ?Type $ flagsType ): Type
6060 {
61- $ inexistentOffsetType = $ this ->hasFlag ($ this -> getConstant ( 'FILTER_NULL_ON_FAILURE ' ) , $ flagsType )
61+ $ inexistentOffsetType = $ this ->hasFlag ('FILTER_NULL_ON_FAILURE ' , $ flagsType )
6262 ? new ConstantBooleanType (false )
6363 : new NullType ();
6464
@@ -107,6 +107,9 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
107107
108108 if ($ filterType === null ) {
109109 $ filterValue = $ this ->getConstant ('FILTER_DEFAULT ' );
110+ if (null === $ filterValue ) {
111+ return $ mixedType ;
112+ }
110113 } else {
111114 if (!$ filterType instanceof ConstantIntegerType) {
112115 return $ mixedType ;
@@ -121,17 +124,17 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
121124 $ hasOptions = $ this ->hasOptions ($ flagsType );
122125 $ options = $ hasOptions ->yes () ? $ this ->getOptions ($ flagsType , $ filterValue ) : [];
123126
124- $ defaultType = $ options ['default ' ] ?? ($ this ->hasFlag ($ this -> getConstant ( 'FILTER_NULL_ON_FAILURE ' ) , $ flagsType )
127+ $ defaultType = $ options ['default ' ] ?? ($ this ->hasFlag ('FILTER_NULL_ON_FAILURE ' , $ flagsType )
125128 ? new NullType ()
126129 : new ConstantBooleanType (false ));
127130
128131 $ inputIsArray = $ inputType ->isArray ();
129- $ hasRequireArrayFlag = $ this ->hasFlag ($ this -> getConstant ( 'FILTER_REQUIRE_ARRAY ' ) , $ flagsType );
132+ $ hasRequireArrayFlag = $ this ->hasFlag ('FILTER_REQUIRE_ARRAY ' , $ flagsType );
130133 if ($ inputIsArray ->no () && $ hasRequireArrayFlag ) {
131134 return $ defaultType ;
132135 }
133136
134- $ hasForceArrayFlag = $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FORCE_ARRAY ' ) , $ flagsType );
137+ $ hasForceArrayFlag = $ this ->hasFlag ('FILTER_FORCE_ARRAY ' , $ flagsType );
135138 if ($ inputIsArray ->yes () && ($ hasRequireArrayFlag || $ hasForceArrayFlag )) {
136139 $ inputArrayKeyType = $ inputType ->getIterableKeyType ();
137140 $ inputType = $ inputType ->getIterableValueType ();
@@ -187,32 +190,46 @@ private function getFilterTypeMap(): array
187190 $ stringType = new StringType ();
188191 $ nonFalsyStringType = TypeCombinator::intersect ($ stringType , new AccessoryNonFalsyStringType ());
189192
190- $ this -> filterTypeMap = [
191- $ this -> getConstant ( 'FILTER_UNSAFE_RAW ' ) => $ stringType ,
192- $ this -> getConstant ( 'FILTER_SANITIZE_EMAIL ' ) => $ stringType ,
193- $ this -> getConstant ( 'FILTER_SANITIZE_ENCODED ' ) => $ stringType ,
194- $ this -> getConstant ( 'FILTER_SANITIZE_NUMBER_FLOAT ' ) => $ stringType ,
195- $ this -> getConstant ( 'FILTER_SANITIZE_NUMBER_INT ' ) => $ stringType ,
196- $ this -> getConstant ( 'FILTER_SANITIZE_SPECIAL_CHARS ' ) => $ stringType ,
197- $ this -> getConstant ( 'FILTER_SANITIZE_STRING ' ) => $ stringType ,
198- $ this -> getConstant ( 'FILTER_SANITIZE_URL ' ) => $ stringType ,
199- $ this -> getConstant ( 'FILTER_VALIDATE_BOOLEAN ' ) => $ booleanType ,
200- $ this -> getConstant ( 'FILTER_VALIDATE_DOMAIN ' ) => $ stringType ,
201- $ this -> getConstant ( 'FILTER_VALIDATE_EMAIL ' ) => $ nonFalsyStringType ,
202- $ this -> getConstant ( 'FILTER_VALIDATE_FLOAT ' ) => $ floatType ,
203- $ this -> getConstant ( 'FILTER_VALIDATE_INT ' ) => $ intType ,
204- $ this -> getConstant ( 'FILTER_VALIDATE_IP ' ) => $ nonFalsyStringType ,
205- $ this -> getConstant ( 'FILTER_VALIDATE_MAC ' ) => $ nonFalsyStringType ,
206- $ this -> getConstant ( 'FILTER_VALIDATE_REGEXP ' ) => $ stringType ,
207- $ this -> getConstant ( 'FILTER_VALIDATE_URL ' ) => $ nonFalsyStringType ,
193+ $ map = [
194+ 'FILTER_UNSAFE_RAW ' => $ stringType ,
195+ 'FILTER_SANITIZE_EMAIL ' => $ stringType ,
196+ 'FILTER_SANITIZE_ENCODED ' => $ stringType ,
197+ 'FILTER_SANITIZE_NUMBER_FLOAT ' => $ stringType ,
198+ 'FILTER_SANITIZE_NUMBER_INT ' => $ stringType ,
199+ 'FILTER_SANITIZE_SPECIAL_CHARS ' => $ stringType ,
200+ 'FILTER_SANITIZE_STRING ' => $ stringType ,
201+ 'FILTER_SANITIZE_URL ' => $ stringType ,
202+ 'FILTER_VALIDATE_BOOLEAN ' => $ booleanType ,
203+ 'FILTER_VALIDATE_DOMAIN ' => $ stringType ,
204+ 'FILTER_VALIDATE_EMAIL ' => $ nonFalsyStringType ,
205+ 'FILTER_VALIDATE_FLOAT ' => $ floatType ,
206+ 'FILTER_VALIDATE_INT ' => $ intType ,
207+ 'FILTER_VALIDATE_IP ' => $ nonFalsyStringType ,
208+ 'FILTER_VALIDATE_MAC ' => $ nonFalsyStringType ,
209+ 'FILTER_VALIDATE_REGEXP ' => $ stringType ,
210+ 'FILTER_VALIDATE_URL ' => $ nonFalsyStringType ,
208211 ];
209212
213+ $ this ->filterTypeMap = [];
214+ foreach ($ map as $ filter => $ type ) {
215+ $ constant = $ this ->getConstant ($ filter );
216+ if (null !== $ constant ) {
217+ $ this ->filterTypeMap [$ constant ] = $ type ;
218+ }
219+ }
220+
210221 if ($ this ->reflectionProvider ->hasConstant (new Node \Name ('FILTER_SANITIZE_MAGIC_QUOTES ' ), null )) {
211- $ this ->filterTypeMap [$ this ->getConstant ('FILTER_SANITIZE_MAGIC_QUOTES ' )] = $ stringType ;
222+ $ sanitizeMagicQuote = $ this ->getConstant ('FILTER_SANITIZE_MAGIC_QUOTES ' );
223+ if (null !== $ sanitizeMagicQuote ) {
224+ $ this ->filterTypeMap [$ sanitizeMagicQuote ] = $ stringType ;
225+ }
212226 }
213227
214228 if ($ this ->reflectionProvider ->hasConstant (new Node \Name ('FILTER_SANITIZE_ADD_SLASHES ' ), null )) {
215- $ this ->filterTypeMap [$ this ->getConstant ('FILTER_SANITIZE_ADD_SLASHES ' )] = $ stringType ;
229+ $ sanitizeAddSlashes = $ this ->getConstant ('FILTER_SANITIZE_ADD_SLASHES ' );
230+ if (null !== $ sanitizeAddSlashes ) {
231+ $ this ->filterTypeMap [$ sanitizeAddSlashes ] = $ stringType ;
232+ }
216233 }
217234
218235 return $ this ->filterTypeMap ;
@@ -227,24 +244,32 @@ private function getFilterTypeOptions(): array
227244 return $ this ->filterTypeOptions ;
228245 }
229246
230- $ this -> filterTypeOptions = [
231- $ this -> getConstant ( 'FILTER_VALIDATE_INT ' ) => ['min_range ' , 'max_range ' ],
247+ $ map = [
248+ 'FILTER_VALIDATE_INT ' => ['min_range ' , 'max_range ' ],
232249 // PHPStan does not yet support FloatRangeType
233- // $this->getConstant( 'FILTER_VALIDATE_FLOAT') => ['min_range', 'max_range'],
250+ // 'FILTER_VALIDATE_FLOAT' => ['min_range', 'max_range'],
234251 ];
235252
253+ $ this ->filterTypeOptions = [];
254+ foreach ($ map as $ filter => $ type ) {
255+ $ constant = $ this ->getConstant ($ filter );
256+ if (null !== $ constant ) {
257+ $ this ->filterTypeOptions [$ constant ] = $ type ;
258+ }
259+ }
260+
236261 return $ this ->filterTypeOptions ;
237262 }
238263
239264 /**
240265 * @param non-empty-string $constantName
241266 */
242- private function getConstant (string $ constantName ): int
267+ private function getConstant (string $ constantName ): ? int
243268 {
244269 $ constant = $ this ->reflectionProvider ->getConstant (new Node \Name ($ constantName ), null );
245270 $ valueType = $ constant ->getValueType ();
246271 if (!$ valueType instanceof ConstantIntegerType) {
247- throw new ShouldNotHappenException ( sprintf ( ' Constant %s does not have integer type. ' , $ constantName )) ;
272+ return null ;
248273 }
249274
250275 return $ valueType ->getValue ();
@@ -301,8 +326,8 @@ private function determineExactType(Type $in, int $filterValue, Type $defaultTyp
301326
302327 if ($ in instanceof ConstantStringType) {
303328 $ value = $ in ->getValue ();
304- $ allowOctal = $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FLAG_ALLOW_OCTAL ' ) , $ flagsType );
305- $ allowHex = $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FLAG_ALLOW_HEX ' ) , $ flagsType );
329+ $ allowOctal = $ this ->hasFlag ('FILTER_FLAG_ALLOW_OCTAL ' , $ flagsType );
330+ $ allowHex = $ this ->hasFlag ('FILTER_FLAG_ALLOW_HEX ' , $ flagsType );
306331
307332 if ($ allowOctal && preg_match ('/\A0[oO][0-7]+\z/ ' , $ value ) === 1 ) {
308333 $ octalValue = octdec ($ value );
@@ -411,8 +436,13 @@ private function getOptions(Type $flagsType, int $filterValue): array
411436 return $ options ;
412437 }
413438
414- private function hasFlag (int $ flag , ?Type $ flagsType ): bool
439+ private function hasFlag (string $ flagName , ?Type $ flagsType ): bool
415440 {
441+ $ flag = $ this ->getConstant ($ flagName );
442+ if (null === $ flag ) {
443+ return false ;
444+ }
445+
416446 if ($ flagsType === null ) {
417447 return false ;
418448 }
@@ -441,9 +471,9 @@ private function canStringBeSanitized(int $filterValue, ?Type $flagsType): bool
441471 // FILTER_DEFAULT will not sanitize, unless it has FILTER_FLAG_STRIP_LOW,
442472 // FILTER_FLAG_STRIP_HIGH, or FILTER_FLAG_STRIP_BACKTICK
443473 if ($ filterValue === $ this ->getConstant ('FILTER_DEFAULT ' )) {
444- return $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FLAG_STRIP_LOW ' ) , $ flagsType )
445- || $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FLAG_STRIP_HIGH ' ) , $ flagsType )
446- || $ this ->hasFlag ($ this -> getConstant ( 'FILTER_FLAG_STRIP_BACKTICK ' ) , $ flagsType );
474+ return $ this ->hasFlag ('FILTER_FLAG_STRIP_LOW ' , $ flagsType )
475+ || $ this ->hasFlag ('FILTER_FLAG_STRIP_HIGH ' , $ flagsType )
476+ || $ this ->hasFlag ('FILTER_FLAG_STRIP_BACKTICK ' , $ flagsType );
447477 }
448478
449479 return true ;
0 commit comments