Skip to content

Commit b7b2ba4

Browse files
authored
Merge branch refs/heads/2.1.x into 2.2.x
2 parents 0e8466f + ff3110d commit b7b2ba4

File tree

6 files changed

+112
-32
lines changed

6 files changed

+112
-32
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PHPStan\DependencyInjection\AutowiredService;
6+
use function array_filter;
7+
use function array_map;
8+
use function array_values;
9+
use function function_exists;
10+
use function in_array;
11+
use function openssl_cipher_iv_length;
12+
use function openssl_get_cipher_methods;
13+
use function strtolower;
14+
15+
#[AutowiredService]
16+
final class OpenSslCipherMethodsProvider
17+
{
18+
19+
/** @var list<string>|null */
20+
private ?array $supportedCipherMethods = null;
21+
22+
/**
23+
* Returns supported cipher methods in lowercase.
24+
*
25+
* Filters out methods that openssl_get_cipher_methods() reports
26+
* but are not actually usable due to https://github.com/php/php-src/issues/19994
27+
*
28+
* @return list<string>
29+
*/
30+
public function getSupportedCipherMethods(): array
31+
{
32+
if ($this->supportedCipherMethods !== null) {
33+
return $this->supportedCipherMethods;
34+
}
35+
36+
$methods = [];
37+
if (function_exists('openssl_get_cipher_methods')) {
38+
// openssl_get_cipher_methods() reports algorithms that are not actually
39+
// supported on PHP 8.0-8.4 due to https://github.com/php/php-src/issues/19994
40+
// Filter by actually testing each algorithm with openssl_cipher_iv_length().
41+
$methods = array_values(array_filter(
42+
openssl_get_cipher_methods(true),
43+
static fn (string $algorithm): bool => @openssl_cipher_iv_length($algorithm) !== false,
44+
));
45+
}
46+
47+
$this->supportedCipherMethods = array_map('strtolower', $methods);
48+
49+
return $this->supportedCipherMethods;
50+
}
51+
52+
public function isSupportedCipherMethod(string $method): bool
53+
{
54+
return in_array(strtolower($method), $this->getSupportedCipherMethods(), true);
55+
}
56+
57+
}

src/Type/Php/OpenSslEncryptParameterOutTypeExtension.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
use PHPStan\Type\TypeCombinator;
1717
use PHPStan\Type\UnionType;
1818
use function in_array;
19-
use function openssl_get_cipher_methods;
2019
use function strtolower;
2120
use function substr;
2221

2322
#[AutowiredService]
2423
final class OpenSslEncryptParameterOutTypeExtension implements FunctionParameterOutTypeExtension
2524
{
2625

26+
public function __construct(private OpenSslCipherMethodsProvider $cipherMethodsProvider)
27+
{
28+
}
29+
2730
public function isFunctionSupported(FunctionReflection $functionReflection, ParameterReflection $parameter): bool
2831
{
2932
return $functionReflection->getName() === 'openssl_encrypt' && $parameter->getName() === 'tag';
@@ -44,7 +47,7 @@ public function getParameterOutTypeFromFunctionCall(FunctionReflection $function
4447
$cipher = strtolower($cipherType->getValue());
4548
$mode = substr($cipher, -3);
4649

47-
if (!in_array($cipher, openssl_get_cipher_methods(), true)) {
50+
if (!$this->cipherMethodsProvider->isSupportedCipherMethod($cipher)) {
4851
$tagTypes[] = new NullType();
4952
continue;
5053
}

src/Type/Php/OpensslCipherFunctionsReturnTypeExtension.php

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616
use function array_map;
1717
use function array_unique;
1818
use function count;
19-
use function function_exists;
2019
use function in_array;
21-
use function is_null;
22-
use function openssl_get_cipher_methods;
23-
use function strtoupper;
2420

2521
#[AutowiredService]
2622
final class OpensslCipherFunctionsReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2723
{
2824

29-
/** @var string[]|null */
30-
private ?array $supportedAlgorithms = null;
31-
32-
public function __construct(private PhpVersion $phpVersion)
25+
public function __construct(
26+
private PhpVersion $phpVersion,
27+
private OpenSslCipherMethodsProvider $cipherMethodsProvider,
28+
)
3329
{
3430
}
3531

@@ -49,7 +45,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
4945
}
5046

5147
$strings = $scope->getType($functionCall->getArgs()[0]->value)->getConstantStrings();
52-
$results = array_unique(array_map(fn (ConstantStringType $algorithm): bool => $this->isSupportedAlgorithm($algorithm->getValue()), $strings));
48+
$results = array_unique(array_map(fn (ConstantStringType $algorithm): bool => $this->cipherMethodsProvider->isSupportedCipherMethod($algorithm->getValue()), $strings));
5349

5450
if (count($results) !== 1) {
5551
return null;
@@ -66,25 +62,4 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
6662
: new ConstantBooleanType(false);
6763
}
6864

69-
private function isSupportedAlgorithm(string $algorithm): bool
70-
{
71-
return in_array(strtoupper($algorithm), $this->getSupportedAlgorithms(), true);
72-
}
73-
74-
/** @return string[] */
75-
private function getSupportedAlgorithms(): array
76-
{
77-
if (!is_null($this->supportedAlgorithms)) {
78-
return $this->supportedAlgorithms;
79-
}
80-
81-
$supportedAlgorithms = [];
82-
if (function_exists('openssl_get_cipher_methods')) {
83-
$supportedAlgorithms = openssl_get_cipher_methods(true);
84-
}
85-
$this->supportedAlgorithms = array_map('strtoupper', $supportedAlgorithms);
86-
87-
return $this->supportedAlgorithms;
88-
}
89-
9065
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ private static function findTestFiles(): iterable
3939
yield __DIR__ . '/data/enum-reflection-backed.php';
4040
}
4141

42+
if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80500) {
43+
yield __DIR__ . '/data/bug-13692.php';
44+
}
45+
4246
if (PHP_VERSION_ID < 80000) {
4347
yield __DIR__ . '/data/bug-4902.php';
4448
}
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 Bug13692;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
public function doFoo(): void
11+
{
12+
// aes-128-cbc-cts is reported by openssl_get_cipher_methods() on PHP 8.0-8.4
13+
// but is not actually supported by openssl_cipher_iv_length() due to a PHP bug
14+
// https://github.com/php/php-src/issues/19994
15+
// On PHP 8.4 where PHPStan runs, this should be refined to false
16+
// (not incorrectly refined to int)
17+
assertType('false', openssl_cipher_iv_length('aes-128-cbc-cts'));
18+
19+
// These should still work correctly
20+
assertType('int', openssl_cipher_iv_length('aes-128-cbc'));
21+
assertType('false', openssl_cipher_iv_length('unknown'));
22+
}
23+
24+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php // lint >= 8.5
2+
3+
namespace Bug13692Php85;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
public function doFoo()
11+
{
12+
assertType('int', openssl_cipher_iv_length('aes-128-cbc-cts'));
13+
assertType('int', openssl_cipher_iv_length('aes-128-cbc'));
14+
assertType('false', openssl_cipher_iv_length('unknown'));
15+
}
16+
17+
}

0 commit comments

Comments
 (0)