diff --git a/conf/config.neon b/conf/config.neon index d77e889531..11dc2ef4c4 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1829,6 +1829,16 @@ services: tags: - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\Php\SocketObjectReturnTypeFunctionExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + + - + class: PHPStan\Type\Php\AddressInfoObjectReturnTypeFunctionExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\StrSplitFunctionReturnTypeExtension tags: diff --git a/src/Php/PhpVersions.php b/src/Php/PhpVersions.php index 229dccb72d..66cc5d8417 100644 --- a/src/Php/PhpVersions.php +++ b/src/Php/PhpVersions.php @@ -33,6 +33,11 @@ public function producesWarningForFinalPrivateMethods(): TrinaryLogic return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; } + public function socketFunctionsUseObject(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; + } + public function supportsNamedArguments(): TrinaryLogic { return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; diff --git a/src/Type/Php/AddressInfoObjectReturnTypeFunctionExtension.php b/src/Type/Php/AddressInfoObjectReturnTypeFunctionExtension.php new file mode 100644 index 0000000000..5aef970fdd --- /dev/null +++ b/src/Type/Php/AddressInfoObjectReturnTypeFunctionExtension.php @@ -0,0 +1,43 @@ +getName(), self::FUNCTIONS, true); + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + if ($scope->getPhpVersion()->socketFunctionsUseObject()->yes()) { + return new UnionType([new ConstantBooleanType(false), new ArrayType(new MixedType(), new ObjectType('\\AddressInfo'))]); + } + + if ($scope->getPhpVersion()->socketFunctionsUseObject()->no()) { + return new UnionType([new ConstantBooleanType(false), new ArrayType(new MixedType(), new ResourceType())]); + } + + return null; + } + +} diff --git a/src/Type/Php/SocketObjectReturnTypeFunctionExtension.php b/src/Type/Php/SocketObjectReturnTypeFunctionExtension.php new file mode 100644 index 0000000000..2307771ec6 --- /dev/null +++ b/src/Type/Php/SocketObjectReturnTypeFunctionExtension.php @@ -0,0 +1,47 @@ +getName(), self::FUNCTIONS, true); + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + if ($scope->getPhpVersion()->socketFunctionsUseObject()->yes()) { + return new UnionType([new ConstantBooleanType(false), new ObjectType('\\Socket')]); + } + + if ($scope->getPhpVersion()->socketFunctionsUseObject()->no()) { + return new UnionType([new ConstantBooleanType(false), new ResourceType()]); + } + + return null; + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/socket-create.php b/tests/PHPStan/Analyser/nsrt/socket-create.php new file mode 100644 index 0000000000..3dad78b931 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/socket-create.php @@ -0,0 +1,32 @@ += 7.4 + +declare(strict_types=1); + +namespace SocketCreate; + +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + public function createSocket(): void + { + if (PHP_VERSION_ID < 80000) { + assertType('resource|false', socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)); + } + + if (PHP_VERSION_ID >= 80000) { + assertType('\Socket|false', socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)); + } + } + + public function addrinfo($host): void + { + if (PHP_VERSION_ID < 80000) { + assertType('array|false', socket_addrinfo_lookup($host)); + } + + if (PHP_VERSION_ID >= 80000) { + assertType('array<\AddressInfo>|false', socket_addrinfo_lookup($host)); + } + } +}