diff --git a/build/baseline-7.4.neon b/build/baseline-7.4.neon index 5206715bf0..c449e95c0f 100644 --- a/build/baseline-7.4.neon +++ b/build/baseline-7.4.neon @@ -94,6 +94,6 @@ parameters: path: ../src/Reflection/ReflectionProvider/SetterReflectionProviderProvider.php - - message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:270 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:271 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: ../src/Testing/TestCase.php diff --git a/conf/config.neon b/conf/config.neon index 3de1ef5ccf..be12f34692 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -650,6 +650,11 @@ services: - implement: PHPStan\Reflection\Php\PhpMethodReflectionFactory + - + class: PHPStan\Reflection\Php\Soap\SoapClientMethodsClassReflectionExtension + tags: + - phpstan.broker.methodsClassReflectionExtension + - class: PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension tags: diff --git a/src/Reflection/Php/Soap/SoapClientMethodReflection.php b/src/Reflection/Php/Soap/SoapClientMethodReflection.php new file mode 100644 index 0000000000..32701b305e --- /dev/null +++ b/src/Reflection/Php/Soap/SoapClientMethodReflection.php @@ -0,0 +1,106 @@ +declaringClass = $declaringClass; + $this->name = $name; + } + + public function getDeclaringClass(): ClassReflection + { + return $this->declaringClass; + } + + public function isStatic(): bool + { + return false; + } + + public function isPrivate(): bool + { + return false; + } + + public function isPublic(): bool + { + return true; + } + + public function getDocComment(): ?string + { + return null; + } + + public function getName(): string + { + return $this->name; + } + + public function getPrototype(): ClassMemberReflection + { + return $this; + } + + public function getVariants(): array + { + return [ + new FunctionVariant( + TemplateTypeMap::createEmpty(), + TemplateTypeMap::createEmpty(), + [], + true, + new MixedType(true) + ), + ]; + } + + public function isDeprecated(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getDeprecatedDescription(): ?string + { + return null; + } + + public function isFinal(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isInternal(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getThrowType(): ?Type + { + return new ObjectType('SoapFault'); + } + + public function hasSideEffects(): TrinaryLogic + { + return TrinaryLogic::createYes(); + } + +} diff --git a/src/Reflection/Php/Soap/SoapClientMethodsClassReflectionExtension.php b/src/Reflection/Php/Soap/SoapClientMethodsClassReflectionExtension.php new file mode 100644 index 0000000000..73924b6081 --- /dev/null +++ b/src/Reflection/Php/Soap/SoapClientMethodsClassReflectionExtension.php @@ -0,0 +1,22 @@ +getName() === 'SoapClient' || $classReflection->isSubclassOf('SoapClient'); + } + + public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection + { + return new SoapClientMethodReflection($classReflection, $methodName); + } + +} diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index 1eafaa2b05..b91ed8ac34 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -60,6 +60,7 @@ use PHPStan\Reflection\Php\PhpFunctionReflection; use PHPStan\Reflection\Php\PhpMethodReflection; use PHPStan\Reflection\Php\PhpMethodReflectionFactory; +use PHPStan\Reflection\Php\Soap\SoapClientMethodsClassReflectionExtension; use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\ReflectionProvider\ClassBlacklistReflectionProvider; @@ -356,6 +357,7 @@ public function create( $classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension($phpExtension); $classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension(new MixinMethodsClassReflectionExtension([])); $classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension($annotationsMethodsClassReflectionExtension); + $classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension(new SoapClientMethodsClassReflectionExtension()); $setterReflectionProviderProvider->setReflectionProvider($actualReflectionProvider); } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index d708be3381..a7b8989a9c 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -387,6 +387,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4838.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4879.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4820.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4822.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-4822.php b/tests/PHPStan/Analyser/data/bug-4822.php new file mode 100644 index 0000000000..2f730eaa45 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4822.php @@ -0,0 +1,33 @@ +test(); + + if (is_array($response)) { + $this->save(); + } + } catch (\Exception $e) { + assertVariableCertainty(TrinaryLogic::createMaybe(), $response); + } + } + +}