diff --git a/conf/config.neon b/conf/config.neon index 099a7e214b..1455cbd909 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1580,6 +1580,11 @@ services: tags: - phpstan.dynamicFunctionThrowTypeExtension + - + class: PHPStan\Type\Php\OpensslCipherFunctionsReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\OpenSslEncryptParameterOutTypeExtension tags: diff --git a/src/Type/Php/OpensslCipherFunctionsReturnTypeExtension.php b/src/Type/Php/OpensslCipherFunctionsReturnTypeExtension.php new file mode 100644 index 0000000000..e13c83f481 --- /dev/null +++ b/src/Type/Php/OpensslCipherFunctionsReturnTypeExtension.php @@ -0,0 +1,88 @@ +getName(), ['openssl_cipher_iv_length', 'openssl_cipher_key_length'], true); + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) { + return null; + } + + if (count($functionCall->getArgs()) < 1) { + return null; + } + + $strings = $scope->getType($functionCall->getArgs()[0]->value)->getConstantStrings(); + $results = array_unique(array_map(fn (ConstantStringType $algorithm): bool => $this->isSupportedAlgorithm($algorithm->getValue()), $strings)); + + if (count($results) !== 1) { + return null; + } + + $returnType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $functionCall->getArgs(), + $functionReflection->getVariants(), + )->getReturnType(); + + return $results[0] + ? TypeCombinator::remove($returnType, new ConstantBooleanType(false)) + : new ConstantBooleanType(false); + } + + private function isSupportedAlgorithm(string $algorithm): bool + { + return in_array(strtoupper($algorithm), $this->getSupportedAlgorithms(), true); + } + + /** @return string[] */ + private function getSupportedAlgorithms(): array + { + if (!is_null($this->supportedAlgorithms)) { + return $this->supportedAlgorithms; + } + + $supportedAlgorithms = []; + if (function_exists('openssl_get_cipher_methods')) { + $supportedAlgorithms = openssl_get_cipher_methods(true); + } + $this->supportedAlgorithms = array_map('strtoupper', $supportedAlgorithms); + + return $this->supportedAlgorithms; + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/openssl-cipher-iv-length-php7.php b/tests/PHPStan/Analyser/nsrt/openssl-cipher-iv-length-php7.php new file mode 100644 index 0000000000..72b8c41b87 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/openssl-cipher-iv-length-php7.php @@ -0,0 +1,24 @@ += 8.0 + +namespace OpensslCipherIvLengthPhp8; + +use function PHPStan\Testing\assertType; + +class OpensslCipher +{ + + /** + * @param 'aes-256-cbc'|'aes128'|'aes-128-cbc' $validAlgorithms + * @param 'aes-256-cbc'|'invalid' $validAndInvalidAlgorithms + */ + public function doFoo(string $s, $validAlgorithms, $validAndInvalidAlgorithms) + { + assertType('int', openssl_cipher_iv_length('aes-256-cbc')); + assertType('int', openssl_cipher_iv_length('AES-256-CBC')); + assertType('false', openssl_cipher_iv_length('unsupported')); + assertType('int|false', openssl_cipher_iv_length($s)); + assertType('int', openssl_cipher_iv_length($validAlgorithms)); + assertType('int|false', openssl_cipher_iv_length($validAndInvalidAlgorithms)); + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/openssl-cipher-key-length.php b/tests/PHPStan/Analyser/nsrt/openssl-cipher-key-length.php new file mode 100644 index 0000000000..c9081ae67c --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/openssl-cipher-key-length.php @@ -0,0 +1,24 @@ += 8.2 + +namespace OpensslCipherKeyLength; + +use function PHPStan\Testing\assertType; + +class OpensslCipher +{ + + /** + * @param 'aes-256-cbc'|'aes128'|'aes-128-cbc' $validAlgorithms + * @param 'aes-256-cbc'|'invalid' $validAndInvalidAlgorithms + */ + public function doFoo(string $s, $validAlgorithms, $validAndInvalidAlgorithms) + { + assertType('int', openssl_cipher_key_length('aes-256-cbc')); + assertType('int', openssl_cipher_key_length('AES-256-CBC')); + assertType('false', openssl_cipher_key_length('unsupported')); + assertType('int|false', openssl_cipher_key_length($s)); + assertType('int', openssl_cipher_key_length($validAlgorithms)); + assertType('int|false', openssl_cipher_key_length($validAndInvalidAlgorithms)); + } + +}