Skip to content

Commit 8970ab6

Browse files
Rework
1 parent 86a908c commit 8970ab6

File tree

6 files changed

+66
-43
lines changed

6 files changed

+66
-43
lines changed

resources/functionMap.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3909,16 +3909,16 @@
39093909
'HaruPage::stroke' => ['bool', 'close_path='=>'bool'],
39103910
'HaruPage::textOut' => ['bool', 'x'=>'float', 'y'=>'float', 'text'=>'string'],
39113911
'HaruPage::textRect' => ['bool', 'left'=>'float', 'top'=>'float', 'right'=>'float', 'bottom'=>'float', 'text'=>'string', 'align='=>'int'],
3912-
'hash' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
3912+
'hash' => ['non-falsy-string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
39133913
'hash_algos' => ['non-empty-list<non-falsy-string>'],
39143914
'hash_copy' => ['HashContext', 'context'=>'HashContext'],
39153915
'hash_equals' => ['bool', 'known_string'=>'string', 'user_string'=>'string'],
3916-
'hash_file' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'filename'=>'string', 'raw_output='=>'bool'],
3917-
'hash_final' => ['non-falsy-string&lowercase-string', 'context'=>'HashContext', 'raw_output='=>'bool'],
3918-
'hash_hkdf' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
3919-
'hash_hmac' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
3916+
'hash_file' => ['non-falsy-string|false', 'algo'=>'string', 'filename'=>'string', 'raw_output='=>'bool'],
3917+
'hash_final' => ['non-falsy-string', 'context'=>'HashContext', 'raw_output='=>'bool'],
3918+
'hash_hkdf' => ['non-falsy-string|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
3919+
'hash_hmac' => ['non-falsy-string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
39203920
'hash_hmac_algos' => ['non-empty-list<non-falsy-string>'],
3921-
'hash_hmac_file' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
3921+
'hash_hmac_file' => ['non-falsy-string|false', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
39223922
'hash_init' => ['HashContext', 'algo'=>'string', 'options='=>'int', 'key='=>'string'],
39233923
'hash_pbkdf2' => ['(non-falsy-string&lowercase-string)|false', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
39243924
'hash_update' => ['bool', 'context'=>'HashContext', 'data'=>'string'],

resources/functionMap_php80delta.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@
5353
'gmmktime' => ['int|false', 'hour'=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
5454
'hash' => ['non-falsy-string', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
5555
'hash_hkdf' => ['non-falsy-string', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
56-
'hash_hmac' => ['non-empty-string', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
57-
'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
56+
'hash_hmac' => ['non-falsy-string', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
57+
'hash_pbkdf2' => ['non-falsy-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
5858
'imageaffine' => ['false|object', 'src'=>'resource', 'affine'=>'array', 'clip='=>'array'],
5959
'imagecreate' => ['__benevolent<GdImage|false>', 'width'=>'int', 'height'=>'int'],
6060
'imagecreatefrombmp' => ['false|object', 'filename'=>'string'],
@@ -192,10 +192,10 @@
192192
'gmmktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
193193
'gmp_random' => ['GMP', 'limiter='=>'int'],
194194
'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'],
195-
'hash' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
196-
'hash_hkdf' => ['non-empty-string|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
197-
'hash_hmac' => ['non-empty-string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
198-
'hash_pbkdf2' => ['non-empty-string|false', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
195+
'hash' => ['non-falsy-string|false', 'algo'=>'string', 'data'=>'string', 'raw_output='=>'bool'],
196+
'hash_hkdf' => ['non-falsy-string|false', 'algo'=>'string', 'key'=>'string', 'length='=>'int', 'info='=>'string', 'salt='=>'string'],
197+
'hash_hmac' => ['non-falsy-string|false', 'algo'=>'string', 'data'=>'string', 'key'=>'string', 'raw_output='=>'bool'],
198+
'hash_pbkdf2' => ['non-falsy-string|false', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'raw_output='=>'bool'],
199199
'hebrevc' => ['string', 'str'=>'string', 'max_chars_per_line='=>'int'],
200200
'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'],
201201
'imageaffine' => ['resource|false', 'src'=>'resource', 'affine'=>'array', 'clip='=>'array'],

src/Type/Php/HashFunctionsReturnTypeExtension.php

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Php\PhpVersion;
88
use PHPStan\Reflection\FunctionReflection;
9-
use PHPStan\Reflection\ParametersAcceptorSelector;
109
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
1110
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
1211
use PHPStan\Type\Constant\ConstantBooleanType;
1312
use PHPStan\Type\Constant\ConstantStringType;
1413
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1514
use PHPStan\Type\IntersectionType;
16-
use PHPStan\Type\MixedType;
1715
use PHPStan\Type\NeverType;
1816
use PHPStan\Type\StringType;
1917
use PHPStan\Type\Type;
2018
use PHPStan\Type\TypeCombinator;
2119
use PHPStan\Type\TypeUtils;
2220
use function array_map;
21+
use function count;
2322
use function hash_algos;
2423
use function in_array;
24+
use function is_bool;
2525
use function strtolower;
2626

2727
final class HashFunctionsReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -31,26 +31,32 @@ final class HashFunctionsReturnTypeExtension implements DynamicFunctionReturnTyp
3131
'hash' => [
3232
'cryptographic' => false,
3333
'possiblyFalse' => false,
34+
'binary' => 2,
3435
],
3536
'hash_file' => [
3637
'cryptographic' => false,
3738
'possiblyFalse' => true,
39+
'binary' => 2,
3840
],
3941
'hash_hkdf' => [
4042
'cryptographic' => true,
4143
'possiblyFalse' => false,
44+
'binary' => true,
4245
],
4346
'hash_hmac' => [
4447
'cryptographic' => true,
4548
'possiblyFalse' => false,
49+
'binary' => 3,
4650
],
4751
'hash_hmac_file' => [
4852
'cryptographic' => true,
4953
'possiblyFalse' => true,
54+
'binary' => 3,
5055
],
5156
'hash_pbkdf2' => [
5257
'cryptographic' => true,
5358
'possiblyFalse' => false,
59+
'binary' => 5,
5460
],
5561
];
5662

@@ -87,50 +93,54 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
8793
return isset(self::SUPPORTED_FUNCTIONS[$name]);
8894
}
8995

90-
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
96+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
9197
{
92-
$defaultReturnType = ParametersAcceptorSelector::selectFromArgs(
93-
$scope,
94-
$functionCall->getArgs(),
95-
$functionReflection->getVariants(),
96-
)->getReturnType();
97-
9898
if (!isset($functionCall->getArgs()[0])) {
99-
return $defaultReturnType;
99+
return null;
100100
}
101101

102-
$algorithmType = $scope->getType($functionCall->getArgs()[0]->value);
103-
if ($algorithmType instanceof MixedType) {
104-
return TypeUtils::toBenevolentUnion($defaultReturnType);
102+
$functionData = self::SUPPORTED_FUNCTIONS[strtolower($functionReflection->getName())];
103+
if (is_bool($functionData['binary'])) {
104+
$binaryType = new ConstantBooleanType($functionData['binary']);
105+
} elseif (isset($functionCall->getArgs()[$functionData['binary']])) {
106+
$binaryType = $scope->getType($functionCall->getArgs()[$functionData['binary']]->value);
107+
} else {
108+
$binaryType = new ConstantBooleanType(false);
109+
}
110+
111+
$stringTypes = [
112+
new StringType(),
113+
new AccessoryNonFalsyStringType(),
114+
];
115+
if ($binaryType->isFalse()->yes()) {
116+
$stringTypes[] = new AccessoryLowercaseStringType();
105117
}
118+
$stringReturnType = new IntersectionType($stringTypes);
106119

120+
$algorithmType = $scope->getType($functionCall->getArgs()[0]->value);
107121
$constantAlgorithmTypes = $algorithmType->getConstantStrings();
122+
if (count($constantAlgorithmTypes) === 0) {
123+
if ($functionData['possiblyFalse']) {
124+
return TypeUtils::toBenevolentUnion(TypeCombinator::union($stringReturnType, new ConstantBooleanType(false)));
125+
}
108126

109-
if ($constantAlgorithmTypes === []) {
110-
return TypeUtils::toBenevolentUnion($defaultReturnType);
127+
return $stringReturnType;
111128
}
112129

113130
$neverType = new NeverType();
114131
$falseType = new ConstantBooleanType(false);
115-
$nonFalsyLowercaseString = new IntersectionType([
116-
new StringType(),
117-
new AccessoryNonFalsyStringType(),
118-
new AccessoryLowercaseStringType(),
119-
]);
120-
121132
$invalidAlgorithmType = $this->phpVersion->throwsValueErrorForInternalFunctions() ? $neverType : $falseType;
122-
$functionData = self::SUPPORTED_FUNCTIONS[strtolower($functionReflection->getName())];
123133

124134
$returnTypes = array_map(
125-
function (ConstantStringType $type) use ($functionData, $nonFalsyLowercaseString, $invalidAlgorithmType) {
135+
function (ConstantStringType $type) use ($functionData, $stringReturnType, $invalidAlgorithmType) {
126136
$algorithm = strtolower($type->getValue());
127137
if (!in_array($algorithm, $this->hashAlgorithms, true)) {
128138
return $invalidAlgorithmType;
129139
}
130140
if ($functionData['cryptographic'] && in_array($algorithm, self::NON_CRYPTOGRAPHIC_ALGORITHMS, true)) {
131141
return $invalidAlgorithmType;
132142
}
133-
return $nonFalsyLowercaseString;
143+
return $stringReturnType;
134144
},
135145
$constantAlgorithmTypes,
136146
);

tests/PHPStan/Analyser/nsrt/hash-functions-74.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public function hash_hmac(string $string): void
1212
assertType('false', hash_hmac('crc32', 'data', 'key'));
1313
assertType('false', hash_hmac('invalid', 'data', 'key'));
1414
assertType('((lowercase-string&non-falsy-string)|false)', hash_hmac($string, 'data', 'key'));
15+
assertType('(non-falsy-string|false)', hash_hmac($string, 'data', 'key', true));
1516
}
1617

1718
public function hash_hmac_file(): void
@@ -24,6 +25,7 @@ public function hash(string $string): void
2425
{
2526
assertType('false', hash('invalid', 'data', false));
2627
assertType('((lowercase-string&non-falsy-string)|false)', hash($string, 'data'));
28+
assertType('(non-falsy-string|false)', hash($string, 'data', true));
2729
}
2830

2931
public function hash_file(): void
@@ -35,14 +37,15 @@ public function hash_hkdf(string $string): void
3537
{
3638
assertType('false', hash_hkdf('crc32', 'key'));
3739
assertType('false', hash_hkdf('invalid', 'key'));
38-
assertType('((lowercase-string&non-falsy-string)|false)', hash_hkdf($string, 'key'));
40+
assertType('(non-falsy-string|false)', hash_hkdf($string, 'key'));
3941
}
4042

4143
public function hash_pbkdf2(string $string): void
4244
{
4345
assertType('false', hash_pbkdf2('crc32', 'password', 'salt', 1000));
4446
assertType('false', hash_pbkdf2('invalid', 'password', 'salt', 1000));
4547
assertType('((lowercase-string&non-falsy-string)|false)', hash_pbkdf2($string, 'password', 'salt', 1000));
48+
assertType('(non-falsy-string|false)', hash_pbkdf2($string, 'password', 'salt', 1000, 0, true));
4649
}
4750

4851
public function caseSensitive()

tests/PHPStan/Analyser/nsrt/hash-functions-80.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public function hash_hmac(string $string): void
1111
{
1212
assertType('*NEVER*', hash_hmac('crc32', 'data', 'key'));
1313
assertType('*NEVER*', hash_hmac('invalid', 'data', 'key'));
14-
assertType('non-empty-string', hash_hmac($string, 'data', 'key'));
14+
assertType('lowercase-string&non-falsy-string', hash_hmac($string, 'data', 'key'));
15+
assertType('non-falsy-string', hash_hmac($string, 'data', 'key', true));
1516
}
1617

1718
public function hash_hmac_file(): void
@@ -23,7 +24,7 @@ public function hash_hmac_file(): void
2324
public function hash(string $string): void
2425
{
2526
assertType('*NEVER*', hash('invalid', 'data', false));
26-
assertType('non-falsy-string', hash($string, 'data'));
27+
assertType('lowercase-string&non-falsy-string', hash($string, 'data'));
2728
}
2829

2930
public function hash_file(): void
@@ -42,7 +43,7 @@ public function hash_pbkdf2(string $string): void
4243
{
4344
assertType('*NEVER*', hash_pbkdf2('crc32', 'password', 'salt', 1000));
4445
assertType('*NEVER*', hash_pbkdf2('invalid', 'password', 'salt', 1000));
45-
assertType('non-empty-string', hash_pbkdf2($string, 'password', 'salt', 1000));
46+
assertType('lowercase-string&non-falsy-string', hash_pbkdf2($string, 'password', 'salt', 1000));
4647
}
4748

4849
public function caseSensitive()

tests/PHPStan/Analyser/nsrt/hash-functions.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,51 @@ class HashFunctionTests
1414
public function hash_hmac(): void
1515
{
1616
assertType('lowercase-string&non-falsy-string', hash_hmac('md5', 'data', 'key'));
17+
assertType('non-falsy-string', hash_hmac('md5', 'data', 'key', true));
1718
assertType('lowercase-string&non-falsy-string', hash_hmac('sha256', 'data', 'key'));
19+
assertType('non-falsy-string', hash_hmac('sha256', 'data', 'key', true));
1820
}
1921

2022
public function hash_hmac_file(string $string): void
2123
{
2224
assertType('(lowercase-string&non-falsy-string)|false', hash_hmac_file('md5', 'filename', 'key'));
25+
assertType('non-falsy-string|false', hash_hmac_file('md5', 'filename', 'key', true));
2326
assertType('(lowercase-string&non-falsy-string)|false', hash_hmac_file('sha256', 'filename', 'key'));
27+
assertType('non-falsy-string|false', hash_hmac_file('sha256', 'filename', 'key', true));
2428
assertType('((lowercase-string&non-falsy-string)|false)', hash_hmac_file($string, 'filename', 'key'));
29+
assertType('(non-falsy-string|false)', hash_hmac_file($string, 'filename', 'key', true));
2530
}
2631

2732
public function hash($mixed): void
2833
{
2934
assertType('lowercase-string&non-falsy-string', hash('sha256', 'data', false));
30-
assertType('lowercase-string&non-falsy-string', hash('sha256', 'data', true));
35+
assertType('non-falsy-string', hash('sha256', 'data', true));
3136
assertType('lowercase-string&non-falsy-string', hash('md5', $mixed, false));
3237
}
3338

3439
public function hash_file(): void
3540
{
3641
assertType('(lowercase-string&non-falsy-string)|false', hash_file('sha256', 'filename', false));
37-
assertType('(lowercase-string&non-falsy-string)|false', hash_file('sha256', 'filename', true));
42+
assertType('non-falsy-string|false', hash_file('sha256', 'filename', true));
3843
assertType('(lowercase-string&non-falsy-string)|false', hash_file('crc32', 'filename'));
44+
assertType('non-falsy-string|false', hash_file('crc32', 'filename', true));
3945
}
4046

4147
public function hash_hkdf(): void
4248
{
43-
assertType('lowercase-string&non-falsy-string', hash_hkdf('sha256', 'key'));
49+
assertType('non-falsy-string', hash_hkdf('sha256', 'key'));
4450
}
4551

4652
public function hash_pbkdf2(): void
4753
{
4854
assertType('lowercase-string&non-falsy-string', hash_pbkdf2('sha256', 'password', 'salt', 1000));
55+
assertType('non-falsy-string', hash_pbkdf2('sha256', 'password', 'salt', 1000, 0, true));
4956
}
5057

5158
public function caseSensitive()
5259
{
5360
assertType('lowercase-string&non-falsy-string', hash('SHA256', 'data'));
61+
assertType('non-falsy-string', hash('SHA256', 'data', true));
5462
}
5563

5664
public function constantStrings(int $type)
@@ -70,6 +78,7 @@ public function constantStrings(int $type)
7078
}
7179

7280
assertType('lowercase-string&non-falsy-string', hash_pbkdf2($algorithm, 'password', 'salt', 1000));
81+
assertType('non-falsy-string', hash_pbkdf2($algorithm, 'password', 'salt', 1000, 0, true));
7382
}
7483

7584
}

0 commit comments

Comments
 (0)