Skip to content

Commit 1e9efb6

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

File tree

5 files changed

+165
-0
lines changed

5 files changed

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