22
33namespace PHPStan \Type \Php ;
44
5+ use Nette \Utils \RegexpException ;
6+ use Nette \Utils \Strings ;
57use PhpParser \Node \Expr \FuncCall ;
68use PHPStan \Analyser \Scope ;
79use PHPStan \Reflection \FunctionReflection ;
2729use function count ;
2830use function is_array ;
2931use function is_int ;
30- use function preg_match ;
3132use function preg_split ;
3233use function strtolower ;
3334
@@ -57,23 +58,29 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5758 $ flagArg = $ args [3 ] ?? null ;
5859 $ patternType = $ scope ->getType ($ patternArg ->value );
5960 $ patternConstantTypes = $ patternType ->getConstantStrings ();
61+ if (count ($ patternConstantTypes ) > 0 ) {
62+ foreach ($ patternConstantTypes as $ patternConstantType ) {
63+ try {
64+ Strings::match ('' , $ patternConstantType ->getValue ());
65+ } catch (RegexpException $ e ) {
66+ return new ErrorType ();
67+ }
68+ }
69+ }
70+
6071 $ subjectType = $ scope ->getType ($ subjectArg ->value );
6172 $ subjectConstantTypes = $ subjectType ->getConstantStrings ();
6273
63- if (
64- count ($ patternConstantTypes ) > 0
65- && @preg_match ($ patternConstantTypes [0 ]->getValue (), '' ) === false
66- ) {
67- return new ErrorType ();
68- }
69-
7074 $ limits = [];
7175 if ($ limitArg === null ) {
7276 $ limits = [-1 ];
7377 } else {
7478 $ limitType = $ scope ->getType ($ limitArg ->value );
79+ if (!$ limitType ->isInteger ()->yes () && !$ limitType ->isString ()->yes ()) {
80+ return new ErrorType ();
81+ }
7582 foreach ($ limitType ->getConstantScalarValues () as $ limit ) {
76- if (!is_int ($ limit )) {
83+ if (!is_int ($ limit ) && ! is_numeric ( $ limit ) ) {
7784 return new ErrorType ();
7885 }
7986 $ limits [] = $ limit ;
@@ -85,15 +92,18 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
8592 $ flags = [0 ];
8693 } else {
8794 $ flagType = $ scope ->getType ($ flagArg ->value );
95+ if (!$ flagType ->isInteger ()->yes () && !$ flagType ->isString ()->yes ()) {
96+ return new ErrorType ();
97+ }
8898 foreach ($ flagType ->getConstantScalarValues () as $ flag ) {
89- if (!is_int ($ flag )) {
99+ if (!is_int ($ flag ) && ! is_numeric ( $ flag ) ) {
90100 return new ErrorType ();
91101 }
92102 $ flags [] = $ flag ;
93103 }
94104 }
95105
96- if (count ($ patternConstantTypes) === 0 || count ( $ subjectConstantTypes ) === 0 ) {
106+ if ($ this -> isPatternOrSubjectEmpty ($ patternConstantTypes, $ subjectConstantTypes )) {
97107 $ returnNonEmptyStrings = $ flagArg !== null && $ this ->bitwiseFlagAnalyser ->bitwiseOrContainsConstant ($ flagArg ->value , $ scope , 'PREG_SPLIT_NO_EMPTY ' )->yes ();
98108 if ($ returnNonEmptyStrings ) {
99109 $ returnStringType = TypeCombinator::intersect (
@@ -148,9 +158,9 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
148158 foreach ($ subjectConstantTypes as $ subjectConstantType ) {
149159 foreach ($ limits as $ limit ) {
150160 foreach ($ flags as $ flag ) {
151- $ result = @preg_split ($ patternConstantType ->getValue (), $ subjectConstantType ->getValue (), $ limit , $ flag );
161+ $ result = @preg_split ($ patternConstantType ->getValue (), $ subjectConstantType ->getValue (), ( int ) $ limit , ( int ) $ flag );
152162 if ($ result === false ) {
153- continue ;
163+ return new ErrorType () ;
154164 }
155165 $ constantArray = ConstantArrayTypeBuilder::createEmpty ();
156166 foreach ($ result as $ key => $ value ) {
@@ -174,4 +184,12 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
174184 return TypeCombinator::union (...$ resultTypes );
175185 }
176186
187+ /**
188+ * @param ConstantStringType[] $patternConstantArray
189+ * @param ConstantStringType[] $subjectConstantArray
190+ * @return bool
191+ */
192+ private function isPatternOrSubjectEmpty (array $ patternConstantArray , array $ subjectConstantArray ): bool {
193+ return count ($ patternConstantArray ) === 0 || count ($ subjectConstantArray ) === 0 ;
194+ }
177195}
0 commit comments