@@ -19,10 +19,6 @@ class PhpStanType
1919 '\OCI-Collection ' ,
2020 ];
2121
22- private bool $ nullable ;
23-
24- private bool $ falsable ;
25-
2622 /**
2723 * @var string[]
2824 */
@@ -42,33 +38,17 @@ public function __construct(string|\SimpleXMLElement $data, bool $writeOnly = fa
4238 $ data = $ regs [1 ];
4339 }
4440
45- //first we try to parse the type string to have a list as clean as possible.
46- $ nullable = false ;
47- $ falsable = false ;
4841 // Let's make the parameter nullable if it is by reference and is used only for writing.
4942 if ($ writeOnly && $ data !== 'resource ' && $ data !== 'mixed ' ) {
5043 $ data .= '|null ' ;
5144 }
5245
5346 $ returnTypes = $ this ->explodeTypes ($ data );
54- //remove 'null' from the list to identify if the signature type should be nullable
55- if (($ nullablePosition = \array_search ('null ' , $ returnTypes , true )) !== false ) {
56- $ nullable = true ;
57- \array_splice ($ returnTypes , (int ) $ nullablePosition , 1 );
58- }
59- //remove 'false' from the list to identify if the function return false on error
60- if (($ falsablePosition = \array_search ('false ' , $ returnTypes , true )) !== false ) {
61- $ falsable = true ;
62- \array_splice ($ returnTypes , (int ) $ falsablePosition , 1 );
63- }
64- $ count = \count ($ returnTypes );
65- if ($ count === 0 ) {
66- $ returnType = '' ;
67- }
47+ $ anyNullable = false ;
6848 foreach ($ returnTypes as &$ returnType ) {
6949 $ returnType = \trim ($ returnType );
7050 if (str_contains ($ returnType , '? ' )) {
71- $ nullable = true ;
51+ $ anyNullable = true ;
7252 $ returnType = \str_replace ('? ' , '' , $ returnType );
7353 }
7454 // remove the parenthesis only if we are not dealing with a callable
@@ -97,9 +77,10 @@ public function __construct(string|\SimpleXMLElement $data, bool $writeOnly = fa
9777
9878 $ returnType = Type::toRootNamespace ($ returnType );
9979 }
80+ if ($ anyNullable ) {
81+ $ returnTypes [] = 'null ' ;
82+ }
10083 $ this ->types = array_unique ($ returnTypes );
101- $ this ->nullable = $ nullable ;
102- $ this ->falsable = $ falsable ;
10384 }
10485
10586 /**
@@ -136,27 +117,26 @@ public static function selectMostUsefulType(
136117 public function getDocBlockType (?ErrorType $ errorType = null ): string
137118 {
138119 $ returnTypes = $ this ->types ;
139- //add back either null or false to the return types unless the target function return null or false on error (only relevant on return type)
140- if ($ this ->falsable && $ errorType !== ErrorType::FALSY ) {
141- $ returnTypes [] = 'false ' ;
142- } elseif ($ this ->nullable && $ errorType !== ErrorType::NULLSY ) {
143- $ returnTypes [] = 'null ' ;
120+ // If we're turning an error marker into an exception, remove
121+ // the error marker from the return types
122+ if (in_array ('false ' , $ returnTypes ) && $ errorType === ErrorType::FALSY ) {
123+ $ returnTypes = array_diff ($ returnTypes , ['false ' ]);
124+ }
125+ if (in_array ('null ' , $ returnTypes ) && $ errorType === ErrorType::NULLSY ) {
126+ $ returnTypes = array_diff ($ returnTypes , ['null ' ]);
144127 }
145128 sort ($ returnTypes );
146129 $ type = join ('| ' , $ returnTypes );
147- if ( $ type === ' bool ' && ! $ this -> nullable && $ errorType === ErrorType:: FALSY ) {
148- // If the function only returns a boolean, since false is for error, true is for success .
149- // Let's replace this with a "void".
130+ // If the function only returns a boolean, since false is for error, true is for success.
131+ // Let's replace this with a "void" .
132+ if ( $ type === ' bool ' && $ errorType === ErrorType:: FALSY ) {
150133 return 'void ' ;
151134 }
152135 return $ type ;
153136 }
154137
155138 public function getSignatureType (?ErrorType $ errorType = null ): string
156139 {
157- //We edit the return type depending of the "onErrorType" of the function. For example, a function that is both nullable and "nullsy" will created a non nullable safe function. Only relevant on return type.
158- $ nullable = $ errorType === ErrorType::NULLSY ? false : $ this ->nullable ;
159- $ falsable = $ errorType === ErrorType::FALSY ? false : $ this ->falsable ;
160140 $ types = $ this ->types ;
161141 //no typehint exists for those cases
162142 if (\array_intersect (self ::NO_SIGNATURE_TYPES , $ types ) !== []) {
@@ -172,8 +152,6 @@ public function getSignatureType(?ErrorType $errorType = null): string
172152 $ type = 'array ' ; //generics cannot be typehinted
173153 } elseif (str_contains ($ type , 'resource ' )) {
174154 $ type = '' ; // resource cant be typehinted
175- } elseif (str_contains ($ type , 'null ' )) {
176- $ type = '' ; // null is a real typehint
177155 } elseif (str_contains ($ type , 'true ' )) {
178156 $ type = 'bool ' ; // php8.1 doesn't support "true" as a typehint
179157 } elseif (str_contains ($ type , 'non-falsy-string ' )) {
@@ -186,10 +164,21 @@ public function getSignatureType(?ErrorType $errorType = null): string
186164 $ types = array_unique ($ types );
187165 sort ($ types );
188166
167+ // If we're turning false/null into exceptions, then
168+ // remove false/null from the return types
169+ if ($ errorType === ErrorType::FALSY ) {
170+ $ types = array_diff ($ types , ['false ' ]);
171+ }
172+ if ($ errorType === ErrorType::NULLSY ) {
173+ $ types = array_diff ($ types , ['null ' ]);
174+ }
175+ // remove "null" from the union so we can add "?" later
176+ $ nullable = in_array ('null ' , $ types );
177+ $ types = array_diff ($ types , ['null ' ]);
189178 if (count ($ types ) === 0 ) {
190179 return '' ;
191180 } elseif (count ($ types ) === 1 ) {
192- $ finalType = $ types [0 ];
181+ $ finalType = array_values ( $ types) [0 ];
193182 if ($ finalType === 'bool ' && !$ nullable && $ errorType === ErrorType::FALSY ) {
194183 // If the function only returns a boolean, since false is for
195184 // error, true is for success. Let's replace this with a "void".
0 commit comments