Skip to content

Commit 6144182

Browse files
committed
Add more precise return types for the openssl cipher functions
1 parent 7644bd0 commit 6144182

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,11 @@ services:
15801580
tags:
15811581
- phpstan.dynamicFunctionThrowTypeExtension
15821582

1583+
-
1584+
class: PHPStan\Type\Php\OpensslCipherFunctionsReturnTypeExtension
1585+
tags:
1586+
- phpstan.broker.dynamicFunctionReturnTypeExtension
1587+
15831588
-
15841589
class: PHPStan\Type\Php\OpenSslEncryptParameterOutTypeExtension
15851590
tags:
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Php\PhpVersion;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\ParametersAcceptorSelector;
10+
use PHPStan\Type\Constant\ConstantBooleanType;
11+
use PHPStan\Type\Constant\ConstantStringType;
12+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
13+
use PHPStan\Type\Type;
14+
use PHPStan\Type\TypeCombinator;
15+
use function array_map;
16+
use function array_unique;
17+
use function count;
18+
use function in_array;
19+
use function is_null;
20+
21+
final class OpensslCipherFunctionsReturnTypeExtension implements DynamicFunctionReturnTypeExtension
22+
{
23+
/** @var string[]|null */
24+
private ?array $supportedAlgorithms = null;
25+
26+
public function __construct(private PhpVersion $phpVersion)
27+
{
28+
}
29+
30+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
31+
{
32+
return in_array($functionReflection->getName(), ['openssl_cipher_iv_length', 'openssl_cipher_key_length'], true);
33+
}
34+
35+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
36+
{
37+
$returnType = ParametersAcceptorSelector::selectFromArgs(
38+
$scope,
39+
$functionCall->getArgs(),
40+
$functionReflection->getVariants(),
41+
)->getReturnType();
42+
43+
if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) {
44+
return $returnType;
45+
}
46+
47+
if (count($functionCall->getArgs()) < 1) {
48+
return $returnType;
49+
}
50+
51+
$strings = $scope->getType($functionCall->getArgs()[0]->value)->getConstantStrings();
52+
$results = array_unique(array_map(fn (ConstantStringType $algorithm): bool => $this->isSupportedAlgorithm($algorithm->getValue()), $strings));
53+
54+
if (count($results) === 1) {
55+
return $results[0]
56+
? TypeCombinator::remove($returnType, new ConstantBooleanType(false))
57+
: new ConstantBooleanType(false);
58+
}
59+
60+
return $returnType;
61+
}
62+
63+
private function isSupportedAlgorithm(string $algorithm): bool
64+
{
65+
return in_array(strtoupper($algorithm), $this->getSupportedAlgorithms(), true);
66+
}
67+
68+
/** @return string[] */
69+
private function getSupportedAlgorithms(): array
70+
{
71+
if (!is_null($this->supportedAlgorithms)) {
72+
return $this->supportedAlgorithms;
73+
}
74+
75+
$supportedAlgorithms = [];
76+
if (function_exists('openssl_get_cipher_methods')) {
77+
$supportedAlgorithms = openssl_get_cipher_methods(true);
78+
}
79+
$this->supportedAlgorithms = array_map('strtoupper', $supportedAlgorithms);
80+
81+
return $this->supportedAlgorithms;
82+
}
83+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php // lint >= 8.0
2+
3+
namespace OpensslCipher;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class OpensslCipher
8+
{
9+
10+
/**
11+
* @param 'aes-256-cbc'|'aes128'|'aes-128-cbc' $validAlgorithms
12+
* @param 'aes-256-cbc'|'invalid' $validAndInvalidAlgorithms
13+
*/
14+
public function doFoo(string $s, $validAlgorithms, $validAndInvalidAlgorithms)
15+
{
16+
assertType('int', openssl_cipher_iv_length('aes-256-cbc'));
17+
assertType('int', openssl_cipher_iv_length('AES-256-CBC'));
18+
assertType('false', openssl_cipher_iv_length('unsupported'));
19+
assertType('int|false', openssl_cipher_iv_length($s));
20+
assertType('int', openssl_cipher_iv_length($validAlgorithms));
21+
assertType('int|false', openssl_cipher_iv_length($validAndInvalidAlgorithms));
22+
23+
assertType('int', openssl_cipher_key_length('aes-256-cbc'));
24+
assertType('int', openssl_cipher_key_length('AES-256-CBC'));
25+
assertType('false', openssl_cipher_key_length('unsupported'));
26+
assertType('int|false', openssl_cipher_key_length($s));
27+
assertType('int', openssl_cipher_key_length($validAlgorithms));
28+
assertType('int|false', openssl_cipher_key_length($validAndInvalidAlgorithms));
29+
}
30+
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php // lint < 8.0
2+
3+
namespace OpensslCipher;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class OpensslCipher
8+
{
9+
10+
/**
11+
* @param 'aes-256-cbc'|'aes128'|'aes-128-cbc' $validAlgorithms
12+
* @param 'aes-256-cbc'|'invalid' $validAndInvalidAlgorithms
13+
*/
14+
public function doFoo(string $s, $validAlgorithms, $validAndInvalidAlgorithms)
15+
{
16+
assertType('int|false', openssl_cipher_iv_length('aes-256-cbc'));
17+
assertType('int|false', openssl_cipher_iv_length('AES-256-CBC'));
18+
assertType('int|false', openssl_cipher_iv_length('unsupported'));
19+
assertType('int|false', openssl_cipher_iv_length($s));
20+
assertType('int|false', openssl_cipher_iv_length($validAlgorithms));
21+
assertType('int|false', openssl_cipher_iv_length($validAndInvalidAlgorithms));
22+
23+
assertType('int|false', openssl_cipher_key_length('aes-256-cbc'));
24+
assertType('int|false', openssl_cipher_key_length('AES-256-CBC'));
25+
assertType('int|false', openssl_cipher_key_length('unsupported'));
26+
assertType('int|false', openssl_cipher_key_length($s));
27+
assertType('int|false', openssl_cipher_key_length($validAlgorithms));
28+
assertType('int|false', openssl_cipher_key_length($validAndInvalidAlgorithms));
29+
}
30+
31+
}

0 commit comments

Comments
 (0)