Skip to content

Commit 163d178

Browse files
stofondrejmirtes
authored andcommitted
Add more precise return types for the openssl cipher functions
1 parent 5c3eed5 commit 163d178

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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 function_exists;
19+
use function in_array;
20+
use function is_null;
21+
use function openssl_get_cipher_methods;
22+
use function strtoupper;
23+
24+
#[AutowiredService]
25+
final class OpensslCipherFunctionsReturnTypeExtension implements DynamicFunctionReturnTypeExtension
26+
{
27+
28+
/** @var string[]|null */
29+
private ?array $supportedAlgorithms = null;
30+
31+
public function __construct(private PhpVersion $phpVersion)
32+
{
33+
}
34+
35+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
36+
{
37+
return in_array($functionReflection->getName(), ['openssl_cipher_iv_length', 'openssl_cipher_key_length'], true);
38+
}
39+
40+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
41+
{
42+
if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) {
43+
return null;
44+
}
45+
46+
if (count($functionCall->getArgs()) < 1) {
47+
return null;
48+
}
49+
50+
$strings = $scope->getType($functionCall->getArgs()[0]->value)->getConstantStrings();
51+
$results = array_unique(array_map(fn (ConstantStringType $algorithm): bool => $this->isSupportedAlgorithm($algorithm->getValue()), $strings));
52+
53+
if (count($results) !== 1) {
54+
return null;
55+
}
56+
57+
$returnType = ParametersAcceptorSelector::selectFromArgs(
58+
$scope,
59+
$functionCall->getArgs(),
60+
$functionReflection->getVariants(),
61+
)->getReturnType();
62+
63+
return $results[0]
64+
? TypeCombinator::remove($returnType, new ConstantBooleanType(false))
65+
: new ConstantBooleanType(false);
66+
}
67+
68+
private function isSupportedAlgorithm(string $algorithm): bool
69+
{
70+
return in_array(strtoupper($algorithm), $this->getSupportedAlgorithms(), true);
71+
}
72+
73+
/** @return string[] */
74+
private function getSupportedAlgorithms(): array
75+
{
76+
if (!is_null($this->supportedAlgorithms)) {
77+
return $this->supportedAlgorithms;
78+
}
79+
80+
$supportedAlgorithms = [];
81+
if (function_exists('openssl_get_cipher_methods')) {
82+
$supportedAlgorithms = openssl_get_cipher_methods(true);
83+
}
84+
$this->supportedAlgorithms = array_map('strtoupper', $supportedAlgorithms);
85+
86+
return $this->supportedAlgorithms;
87+
}
88+
89+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php // lint < 8.0
2+
3+
namespace OpensslCipherIvLengthPhp7;
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+
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php // lint >= 8.0
2+
3+
namespace OpensslCipherIvLengthPhp8;
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+
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php // lint >= 8.2
2+
3+
namespace OpensslCipherKeyLength;
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_key_length('aes-256-cbc'));
17+
assertType('int', openssl_cipher_key_length('AES-256-CBC'));
18+
assertType('false', openssl_cipher_key_length('unsupported'));
19+
assertType('int|false', openssl_cipher_key_length($s));
20+
assertType('int', openssl_cipher_key_length($validAlgorithms));
21+
assertType('int|false', openssl_cipher_key_length($validAndInvalidAlgorithms));
22+
}
23+
24+
}

0 commit comments

Comments
 (0)