33namespace  PHPStan \Type \Php ;
44
55use  DateInterval ;
6+ use  DateTimeImmutable ;
67use  PhpParser \Node \Expr \MethodCall ;
78use  PHPStan \Analyser \Scope ;
89use  PHPStan \DependencyInjection \AutowiredService ;
1213use  PHPStan \Type \Accessory \AccessoryNonFalsyStringType ;
1314use  PHPStan \Type \Accessory \AccessoryNumericStringType ;
1415use  PHPStan \Type \Accessory \AccessoryUppercaseStringType ;
16+ use  PHPStan \Type \Constant \ConstantStringType ;
1517use  PHPStan \Type \DynamicMethodReturnTypeExtension ;
1618use  PHPStan \Type \IntersectionType ;
1719use  PHPStan \Type \StringType ;
1820use  PHPStan \Type \Type ;
1921use  PHPStan \Type \TypeCombinator ;
22+ use  PHPStan \Type \UnionType ;
2023use  function  count ;
2124use  function  is_numeric ;
2225use  function  strtolower ;
@@ -55,12 +58,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
5558			return  null ;
5659		}
5760
58- 		// The worst case scenario for the non-falsy-string check is that every number is 0. 
59- 		$ dateIntervalnew  DateInterval ('P0D ' );
61+ 		$ dateInterval$ this referenceDateInterval ();
6062
6163		$ possibleReturnTypes
6264		foreach  ($ constantStringsas  $ string
63- 			$ value$ dateIntervalformat ($ stringgetValue ());
65+ 			$ formatString$ stringgetValue ();
66+ 			$ value$ dateIntervalformat ($ formatString
6467
6568			$ accessories
6669			if  (is_numeric ($ value
@@ -77,15 +80,42 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
7780			if  (strtoupper ($ value$ value
7881				$ accessoriesnew  AccessoryUppercaseStringType ();
7982			}
83+ 			$ diffInDaysType$ this diffInDaysTypes ($ formatString
8084
81- 			if  (count ($ accessories0 ) {
85+ 			if  (count ($ accessories0  &&  $ diffInDaysType  ===  null ) {
8286				return  null ;
8387			}
8488
85- 			$ possibleReturnTypesnew  IntersectionType ([new  StringType (), ...$ accessories
89+ 			$ intersectionTypenew  IntersectionType ([
90+ 				new  StringType (),
91+ 				...$ accessories
92+ 			]);
93+ 			$ possibleReturnTypes$ diffInDaysTypenull 
94+ 				? $ intersectionType
95+ 				: new  UnionType ([$ diffInDaysType$ intersectionType
8696		}
8797
8898		return  TypeCombinator::union (...$ possibleReturnTypes
8999	}
90100
101+ 	/** 
102+ 	 * The worst case scenario for the non-falsy-string check is that every number is 0. 
103+ 	 * We create an interval from a difference of two DateTime instances due to the different behavior for %a 
104+ 	 * 
105+ 	 * @see https://www.php.net/manual/en/dateinterval.format.php 
106+ 	 * 
107+ 	 * @return DateInterval 
108+ 	 */ 
109+ 	private  function  referenceDateInterval (): DateInterval 
110+ 	{
111+ 		return  (new  DateTimeImmutable ('@0 ' ))->diff ((new  DateTimeImmutable ('@0 ' )));
112+ 	}
113+ 114+ 	private  function  diffInDaysTypes (string  $ formatStringConstantStringType 
115+ 	{
116+ 		return  $ formatString'%a ' 
117+ 			? new  ConstantStringType ('(unknown) ' )
118+ 			: null ;
119+ 	}
120+ 91121}
0 commit comments