Skip to content

Commit 1d63c05

Browse files
authored
Merge branch refs/heads/1.12.x into 2.1.x
2 parents 0520107 + fa6fde1 commit 1d63c05

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

conf/config.neon

+5
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,11 @@ services:
15131513
tags:
15141514
- phpstan.dynamicFunctionThrowTypeExtension
15151515

1516+
-
1517+
class: PHPStan\Type\Php\OpenSslEncryptParameterOutTypeExtension
1518+
tags:
1519+
- phpstan.functionParameterOutTypeExtension
1520+
15161521
-
15171522
class: PHPStan\Type\Php\ParseStrParameterOutTypeExtension
15181523
tags:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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\Reflection\FunctionReflection;
8+
use PHPStan\Reflection\ParameterReflection;
9+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
10+
use PHPStan\Type\FunctionParameterOutTypeExtension;
11+
use PHPStan\Type\NullType;
12+
use PHPStan\Type\StringType;
13+
use PHPStan\Type\Type;
14+
use PHPStan\Type\TypeCombinator;
15+
use function in_array;
16+
use function openssl_get_cipher_methods;
17+
use function strtolower;
18+
use function substr;
19+
20+
final class OpenSslEncryptParameterOutTypeExtension implements FunctionParameterOutTypeExtension
21+
{
22+
23+
public function isFunctionSupported(FunctionReflection $functionReflection, ParameterReflection $parameter): bool
24+
{
25+
return $functionReflection->getName() === 'openssl_encrypt' && $parameter->getName() === 'tag';
26+
}
27+
28+
public function getParameterOutTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $funcCall, ParameterReflection $parameter, Scope $scope): ?Type
29+
{
30+
$args = $funcCall->getArgs();
31+
$cipherArg = $args[1] ?? null;
32+
33+
if ($cipherArg === null) {
34+
return null;
35+
}
36+
37+
$tagTypes = [];
38+
39+
foreach ($scope->getType($cipherArg->value)->getConstantStrings() as $cipherType) {
40+
$cipher = strtolower($cipherType->getValue());
41+
$mode = substr($cipher, -3);
42+
43+
if (!in_array($cipher, openssl_get_cipher_methods(), true)) {
44+
$tagTypes[] = new NullType();
45+
continue;
46+
}
47+
48+
if (in_array($mode, ['gcm', 'ccm'], true)) {
49+
$tagTypes[] = TypeCombinator::intersect(
50+
new StringType(),
51+
new AccessoryNonEmptyStringType(),
52+
);
53+
54+
continue;
55+
}
56+
57+
$tagTypes[] = new NullType();
58+
}
59+
60+
if ($tagTypes === []) {
61+
return TypeCombinator::addNull(TypeCombinator::intersect(
62+
new StringType(),
63+
new AccessoryNonEmptyStringType(),
64+
));
65+
}
66+
67+
return TypeCombinator::union(...$tagTypes);
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace OpenSslEncrypt;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
class Foo
10+
{
11+
public function testStringCipher(string $cipher): void
12+
{
13+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
14+
assertType('non-empty-string|null', $tag);
15+
}
16+
17+
public function testUnknownCipher(): void
18+
{
19+
openssl_encrypt('data', 'aes-256-cde', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
20+
assertType('null', $tag);
21+
22+
openssl_encrypt('data', 'abc-256-gcm', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
23+
assertType('null', $tag);
24+
25+
openssl_encrypt('data', 'abc-256-ccm', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
26+
assertType('null', $tag);
27+
}
28+
29+
public function testAeadCipher(): void
30+
{
31+
$cipher = 'aes-256-gcm';
32+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
33+
assertType('non-empty-string', $tag);
34+
35+
$cipher = 'aes-256-ccm';
36+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
37+
assertType('non-empty-string', $tag);
38+
}
39+
40+
public function testNonAeadCipher(): void
41+
{
42+
$cipher = 'aes-256-cbc';
43+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
44+
assertType('null', $tag);
45+
}
46+
47+
/**
48+
* @param 'aes-256-ctr'|'aes-256-gcm' $cipher
49+
*/
50+
public function testMixedAeadAndNonAeadCiphers(string $cipher): void
51+
{
52+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
53+
assertType('non-empty-string|null', $tag);
54+
}
55+
56+
/**
57+
* @param 'aes-256-cbc'|'aes-256-ctr' $cipher
58+
*/
59+
public function testMixedTwoNonAeadCiphers(string $cipher): void
60+
{
61+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
62+
assertType('null', $tag);
63+
}
64+
65+
/**
66+
* @param 'aes-256-gcm'|'aes-256-ccm' $cipher
67+
*/
68+
public function testMixedTwoAeadCiphers(string $cipher): void
69+
{
70+
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
71+
assertType('non-empty-string', $tag);
72+
}
73+
}

0 commit comments

Comments
 (0)