Skip to content

Commit 9a15690

Browse files
authored
Types: be more specific about required scalar types (#185)
* Types: be more specific about required scalar types * EditorConfig: phpstan-baseline.neon is a generated file
1 parent 8523305 commit 9a15690

16 files changed

+292
-68
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ indent_style = tab
2727
[infection.txt]
2828
indent_size = unset
2929
trim_trailing_whitespace = unset
30+
31+
[phpstan-baseline.neon]
32+
indent_style = unset

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
/infection.json.dist export-ignore
1313
/Makefile export-ignore
1414
/phpstan.neon export-ignore
15+
/phpstan-baseline.neon export-ignore
1516
/phpunit.xml.dist export-ignore
1617
/rector.php export-ignore

phpstan-baseline.neon

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Cannot cast mixed to int\\.$#"
5+
count: 1
6+
path: src/HOTP.php
7+
8+
-
9+
message: "#^Comparison operation \"\\>\\=\" between int\\<0, max\\>\\|null and 0 is always true\\.$#"
10+
count: 1
11+
path: src/HOTP.php
12+
13+
-
14+
message: "#^Method OTPHP\\\\OTP\\:\\:generateSecret\\(\\) should return non\\-empty\\-string but returns string\\.$#"
15+
count: 1
16+
path: src/OTP.php
17+
18+
-
19+
message: "#^Variable property access on \\$this\\(OTPHP\\\\OTP\\)\\.$#"
20+
count: 1
21+
path: src/OTP.php
22+
23+
-
24+
message: "#^Comparison operation \"\\>\\=\" between int\\<0, max\\> and 0 is always true\\.$#"
25+
count: 1
26+
path: src/TOTP.php
27+
28+
-
29+
message: "#^Method OTPHP\\\\TOTP\\:\\:expiresIn\\(\\) should return int\\<0, max\\> but returns int\\.$#"
30+
count: 1
31+
path: src/TOTP.php
32+
33+
-
34+
message: "#^Parameter \\#1 \\$epoch of method OTPHP\\\\TOTP\\:\\:setEpoch\\(\\) expects int\\<0, max\\>, int given\\.$#"
35+
count: 1
36+
path: src/TOTP.php
37+
38+
-
39+
message: "#^Parameter \\#1 \\$input of method OTPHP\\\\TOTP\\:\\:at\\(\\) expects int\\<0, max\\>, int given\\.$#"
40+
count: 1
41+
path: src/TOTP.php
42+
43+
-
44+
message: "#^Left side of \\|\\| is always true\\.$#"
45+
count: 1
46+
path: src/Url.php
47+
48+
-
49+
message: "#^Parameter \\#2 \\$host of class OTPHP\\\\Url constructor expects non\\-empty\\-string, string given\\.$#"
50+
count: 1
51+
path: src/Url.php
52+
53+
-
54+
message: "#^Parameter \\#3 \\$path of class OTPHP\\\\Url constructor expects non\\-empty\\-string, string given\\.$#"
55+
count: 1
56+
path: src/Url.php
57+
58+
-
59+
message: "#^Parameter \\#4 \\$secret of class OTPHP\\\\Url constructor expects non\\-empty\\-string, array\\|string given\\.$#"
60+
count: 1
61+
path: src/Url.php
62+
63+
-
64+
message: "#^Parameter \\#5 \\$query of class OTPHP\\\\Url constructor expects array\\<non\\-empty\\-string, mixed\\>, array\\<int\\|string, array\\|string\\> given\\.$#"
65+
count: 1
66+
path: src/Url.php
67+
68+
-
69+
message: "#^Parameter \\#1 \\$counter of method OTPHP\\\\HOTP\\:\\:setCounter\\(\\) expects int\\<0, max\\>, \\-500 given\\.$#"
70+
count: 1
71+
path: tests/HOTPTest.php
72+
73+
-
74+
message: "#^Parameter \\#1 \\$digits of method OTPHP\\\\OTP\\:\\:setDigits\\(\\) expects int\\<1, max\\>, 0 given\\.$#"
75+
count: 1
76+
path: tests/HOTPTest.php
77+
78+
-
79+
message: "#^Parameter \\#1 \\$epoch of method OTPHP\\\\TOTP\\:\\:setEpoch\\(\\) expects int\\<0, max\\>, \\-1 given\\.$#"
80+
count: 1
81+
path: tests/TOTPTest.php
82+
83+
-
84+
message: "#^Parameter \\#1 \\$period of method OTPHP\\\\TOTP\\:\\:setPeriod\\(\\) expects int\\<1, max\\>, \\-20 given\\.$#"
85+
count: 1
86+
path: tests/TOTPTest.php

phpstan.neon

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,6 @@ parameters:
33
paths:
44
- src
55
- tests
6-
ignoreErrors:
7-
-
8-
message: '#Variable property access on \$this\(OTPHP\\OTP\)\.#'
9-
path: src/ParameterTrait.php
10-
count: 1
11-
-
12-
message: '#^Method OTPHP\\OTP::generateSecret\(\) should return non-empty-string but returns string\.$#'
13-
path: src/OTP.php
14-
count: 1
15-
-
16-
message: '#^Cannot cast mixed to int\.$#'
17-
path: src/HOTP.php
18-
count: 1
19-
-
20-
message: '#^Parameter \#\d .* of class OTPHP\\Url constructor expects .*\, .* given\.$#'
21-
path: src/Url.php
22-
count: 2
236

247
includes:
258
- vendor/phpstan/phpstan/conf/bleedingEdge.neon
@@ -28,3 +11,4 @@ includes:
2811
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
2912
- vendor/phpstan/phpstan-phpunit/rules.neon
3013
- vendor/ekino/phpstan-banned-code/extension.neon
14+
- phpstan-baseline.neon

src/Factory.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace OTPHP;
66

7+
use function assert;
78
use function count;
89
use InvalidArgumentException;
910
use Throwable;
@@ -55,6 +56,9 @@ private static function populateOTP(OTPInterface $otp, Url $data): void
5556
);
5657
$otp->setIssuerIncludedAsParameter(true);
5758
}
59+
60+
assert($result[0] !== '');
61+
5862
$otp->setIssuer($result[0]);
5963
}
6064

@@ -76,10 +80,16 @@ private static function createOTP(Url $parsed_url): OTPInterface
7680
}
7781
}
7882

83+
/**
84+
* @param non-empty-string $data
85+
* @return non-empty-string
86+
*/
7987
private static function getLabel(string $data): string
8088
{
8189
$result = explode(':', rawurldecode(mb_substr($data, 1)));
90+
$label = count($result) === 2 ? $result[1] : $result[0];
91+
assert($label !== '');
8292

83-
return count($result) === 2 ? $result[1] : $result[0];
93+
return $label;
8494
}
8595
}

src/FactoryInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ interface FactoryInterface
99
/**
1010
* This method is the unique public method of the class. It can load a provisioning Uri and convert it into an OTP
1111
* object.
12+
*
13+
* @param non-empty-string $uri
1214
*/
1315
public static function loadFromProvisioningUri(string $uri): OTPInterface;
1416
}

src/HOTP.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static function generate(): self
4949
public function getCounter(): int
5050
{
5151
$value = $this->getParameter('counter');
52-
is_int($value) || throw new InvalidArgumentException('Invalid "counter" parameter.');
52+
(is_int($value) && $value >= 0) || throw new InvalidArgumentException('Invalid "counter" parameter.');
5353

5454
return $value;
5555
}
@@ -83,7 +83,7 @@ public function setCounter(int $counter): void
8383
}
8484

8585
/**
86-
* @return array<string, callable>
86+
* @return array<non-empty-string, callable>
8787
*/
8888
protected function getParameterMap(): array
8989
{
@@ -97,16 +97,27 @@ protected function getParameterMap(): array
9797
]];
9898
}
9999

100+
/**
101+
* @param positive-int $counter
102+
*/
100103
private function updateCounter(int $counter): void
101104
{
102105
$this->setCounter($counter);
103106
}
104107

108+
/**
109+
* @param null|0|positive-int $window
110+
*/
105111
private function getWindow(null|int $window): int
106112
{
107113
return abs($window ?? self::DEFAULT_WINDOW);
108114
}
109115

116+
/**
117+
* @param non-empty-string $otp
118+
* @param 0|positive-int $counter
119+
* @param null|0|positive-int $window
120+
*/
110121
private function verifyOtpWithWindow(string $otp, int $counter, null|int $window): bool
111122
{
112123
$window = $this->getWindow($window);

src/HOTPInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ interface HOTPInterface extends OTPInterface
1010

1111
/**
1212
* The initial counter (a positive integer).
13+
*
14+
* @return 0|positive-int
1315
*/
1416
public function getCounter(): int;
1517

@@ -19,7 +21,9 @@ public function getCounter(): int;
1921
* If the secret is null, a random 64 bytes secret will be generated.
2022
*
2123
* @param null|non-empty-string $secret
24+
* @param 0|positive-int $counter
2225
* @param non-empty-string $digest
26+
* @param positive-int $digits
2327
*
2428
* @deprecated Deprecated since v11.1, use ::createFromSecret or ::generate instead
2529
*/
@@ -30,5 +34,8 @@ public static function create(
3034
int $digits = 6
3135
): self;
3236

37+
/**
38+
* @param 0|positive-int $counter
39+
*/
3340
public function setCounter(int $counter): void;
3441
}

src/OTP.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace OTPHP;
66

7+
use function assert;
78
use function chr;
89
use function count;
910
use Exception;
@@ -49,6 +50,10 @@ final protected static function generateSecret(): string
4950

5051
/**
5152
* The OTP at the specified input.
53+
*
54+
* @param 0|positive-int $input
55+
*
56+
* @return non-empty-string
5257
*/
5358
protected function generateOTP(int $input): string
5459
{
@@ -65,7 +70,7 @@ protected function generateOTP(int $input): string
6570
}
6671

6772
/**
68-
* @param array<string, mixed> $options
73+
* @param array<non-empty-string, mixed> $options
6974
*/
7075
protected function filterOptions(array &$options): void
7176
{
@@ -83,7 +88,10 @@ protected function filterOptions(array &$options): void
8388
}
8489

8590
/**
86-
* @param array<string, mixed> $options
91+
* @param non-empty-string $type
92+
* @param array<non-empty-string, mixed> $options
93+
*
94+
* @return non-empty-string
8795
*/
8896
protected function generateURI(string $type, array $options): string
8997
{
@@ -102,20 +110,33 @@ protected function generateURI(string $type, array $options): string
102110
);
103111
}
104112

113+
/**
114+
* @param non-empty-string $safe
115+
* @param non-empty-string $user
116+
*/
105117
protected function compareOTP(string $safe, string $user): bool
106118
{
107119
return hash_equals($safe, $user);
108120
}
109121

122+
/**
123+
* @return non-empty-string
124+
*/
110125
private function getDecodedSecret(): string
111126
{
112127
try {
113-
return Base32::decodeUpper($this->getSecret());
128+
$decoded = Base32::decodeUpper($this->getSecret());
114129
} catch (Exception) {
115130
throw new RuntimeException('Unable to decode the secret. Is it correctly base32 encoded?');
116131
}
132+
assert($decoded !== '');
133+
134+
return $decoded;
117135
}
118136

137+
/**
138+
* @param 0|positive-int $int
139+
*/
119140
private function intToByteString(int $int): string
120141
{
121142
$result = [];

0 commit comments

Comments
 (0)