Skip to content

Commit 8015b98

Browse files
committed
Fix PHPStan errors
1 parent 79f0ea9 commit 8015b98

10 files changed

+79
-42
lines changed

phpstan-baseline.neon

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Parameter \\#1 \\$hash of method Rollerworks\\\\Component\\\\SplitToken\\\\SplitToken\\:\\:verifyHash\\(\\) expects string, string\\|null given\\.$#"
5+
count: 1
6+
path: src/SplitToken.php
7+
8+
-
9+
message: "#^Parameter \\#1 \\$selector of class Rollerworks\\\\Component\\\\SplitToken\\\\SplitTokenValueHolder constructor expects string, string\\|null given\\.$#"
10+
count: 1
11+
path: src/SplitTokenValueHolder.php
12+
13+
-
14+
message: "#^Parameter \\#2 \\$verifierHash of class Rollerworks\\\\Component\\\\SplitToken\\\\SplitTokenValueHolder constructor expects string, string\\|null given\\.$#"
15+
count: 1
16+
path: src/SplitTokenValueHolder.php

phpstan.neon

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
includes:
22
- vendor/rollerscapes/standards/phpstan.neon
3-
#- phpstan-baseline.neon
3+
- phpstan-baseline.neon
44

55
parameters:
66
#reportUnmatchedIgnoredErrors: false
@@ -13,4 +13,5 @@ parameters:
1313
- templates/
1414
- translations/
1515

16-
#ignoreErrors:
16+
ignoreErrors:
17+
- '#Attribute class Symfony\\Contracts\\Service\\Attribute\\Required does not exist#' # Not required

src/Argon2SplitToken.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ protected function verifyHash(string $hash, string $verifier): bool
3737
/** @codeCoverageIgnore */
3838
protected function hashVerifier(string $verifier): string
3939
{
40-
$passwordHash = password_hash($verifier, \PASSWORD_ARGON2ID, $this->config);
41-
42-
if ($passwordHash === false || $passwordHash === null) {
43-
throw new \RuntimeException('Unrecoverable password hashing error.');
40+
try {
41+
$passwordHash = password_hash($verifier, \PASSWORD_ARGON2ID, $this->config);
42+
} catch (\Throwable $e) {
43+
throw new \RuntimeException('Unrecoverable password hashing error.', 0, $e);
4444
}
4545

4646
return $passwordHash;

src/Argon2SplitTokenFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
final class Argon2SplitTokenFactory extends AbstractSplitTokenFactory
2525
{
2626
/** @param array<string, int> $config */
27-
public function __construct(private array $config = [], \DateInterval | string | null $defaultLifeTime = null)
27+
public function __construct(private array $config = [], \DateInterval | string $defaultLifeTime = null)
2828
{
2929
parent::__construct($defaultLifeTime);
3030
}

src/FakeSplitTokenFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class FakeSplitTokenFactory extends AbstractSplitTokenFactory
2323
public const VERIFIER = '_OR6OOnV1o8Vy_rWhDoxKNIt';
2424
public const FULL_TOKEN = self::SELECTOR . self::VERIFIER;
2525

26-
private $randomValue;
26+
private string $randomValue;
2727

2828
public static function randomInstance(): self
2929
{
@@ -34,7 +34,7 @@ public function __construct(string $randomValue = null, \DateInterval | string $
3434
{
3535
parent::__construct($defaultLifeTime);
3636

37-
$this->randomValue = $randomValue ?? hex2bin('d7351e5d4bebe0b2b298034107f6cb12a88fe463ebf8f85afce47a38e9d5d68f15cbfad6843a3128d22d');
37+
$this->randomValue = $randomValue ?? ((string) hex2bin('d7351e5d4bebe0b2b298034107f6cb12a88fe463ebf8f85afce47a38e9d5d68f15cbfad6843a3128d22d'));
3838
}
3939

4040
public function generate(\DateTimeImmutable | \DateInterval $expiresAt = null): SplitToken

src/SplitToken.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ abstract class SplitToken
9898
private ?string $verifierHash = null;
9999
private ?\DateTimeImmutable $expiresAt = null;
100100

101-
private function __construct(HiddenString $token, string $selector, string $verifier)
101+
final private function __construct(HiddenString $token, string $selector, string $verifier)
102102
{
103103
$this->token = $token;
104104
$this->selector = $selector;
@@ -189,7 +189,7 @@ public function token(): HiddenString
189189
*/
190190
final public function matches(?SplitTokenValueHolder $token): bool
191191
{
192-
if (SplitTokenValueHolder::isEmpty($token)) {
192+
if ($token === null || SplitTokenValueHolder::isEmpty($token)) {
193193
return false;
194194
}
195195

@@ -235,6 +235,8 @@ public function getExpirationTime(): ?\DateTimeImmutable
235235
/**
236236
* This method is called in create() before the verifier is hashed,
237237
* allowing to set-up configuration for the hashing method.
238+
*
239+
* @param array<string, mixed> $config
238240
*/
239241
protected function configureHasher(array $config): void
240242
{

src/SplitTokenValueHolder.php

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@
2727
*/
2828
final class SplitTokenValueHolder
2929
{
30-
private $selector;
31-
private $verifierHash;
32-
private $expiresAt;
33-
private $metadata = [];
30+
private ?string $selector = null;
31+
private ?string $verifierHash = null;
32+
private ?\DateTimeImmutable $expiresAt = null;
33+
/** @var array<string, scalar> */
34+
private array $metadata = [];
3435

36+
/** @param array<string, scalar> $metadata */
3537
public function __construct(string $selector, string $verifierHash, \DateTimeImmutable $expiresAt = null, array $metadata = [])
3638
{
3739
$this->selector = $selector;
@@ -46,18 +48,22 @@ public static function isEmpty(?self $valueHolder): bool
4648
return true;
4749
}
4850

51+
// It's possible these values are empty when used as Embedded, because Embedded
52+
// will always produce an object.
4953
return $valueHolder->selector === null || $valueHolder->verifierHash === null;
5054
}
5155

5256
/**
5357
* Returns whether the current token (if any) can be replaced with the new token.
5458
*
5559
* This methods should only to be used to prevent setting a token when a token
56-
* was already set, which has not expired, and the same metadata was given (type checked!).
60+
* was already set, which has not expired, and the same metadata was given (strict checked!).
61+
*
62+
* @param array<string, scalar> $expectedMetadata
5763
*/
5864
public static function mayReplaceCurrentToken(?self $valueHolder, array $expectedMetadata = []): bool
5965
{
60-
if (self::isEmpty($valueHolder)) {
66+
if ($valueHolder === null || self::isEmpty($valueHolder)) {
6167
return true;
6268
}
6369

@@ -78,23 +84,29 @@ public function verifierHash(): ?string
7884
return $this->verifierHash;
7985
}
8086

87+
/** @param array<string, scalar> $metadata */
8188
public function withMetadata(array $metadata): self
8289
{
90+
if (self::isEmpty($this)) {
91+
throw new \RuntimeException('Incomplete TokenValueHolder.');
92+
}
93+
8394
return new self($this->selector, $this->verifierHash, $this->expiresAt, $metadata);
8495
}
8596

97+
/** @return array<string, scalar> */
8698
public function metadata(): array
8799
{
88100
return $this->metadata ?? [];
89101
}
90102

91-
public function isExpired(\DateTimeImmutable $datetime = null): bool
103+
public function isExpired(\DateTimeImmutable $now = null): bool
92104
{
93105
if ($this->expiresAt === null) {
94106
return false;
95107
}
96108

97-
return $this->expiresAt->getTimestamp() < ($datetime ?? new \DateTimeImmutable())->getTimestamp();
109+
return $this->expiresAt->getTimestamp() < ($now ?? new \DateTimeImmutable())->getTimestamp();
98110
}
99111

100112
public function expiresAt(): ?\DateTimeImmutable
@@ -106,6 +118,7 @@ public function expiresAt(): ?\DateTimeImmutable
106118
* Compares if both objects are the same.
107119
*
108120
* Warning this method leaks timing information and the expiration date is ignored!
121+
* This method should only be used to check if a new token is provided.
109122
*/
110123
public function equals(self $other): bool
111124
{

tests/Argon2SplitTokenFactoryTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function it_generates_with_default_expiration_as_string(): void
6262

6363
private static function assertExpirationEquals(string $expected, SplitToken $actual): void
6464
{
65+
self::assertNotNull($actual->getExpirationTime());
6566
self::assertSame($expected, $actual->getExpirationTime()->format('Y-m-d\TH:i:s'));
6667
}
6768

tests/Argon2SplitTokenTest.php

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ final class Argon2SplitTokenTest extends TestCase
2424
private const FULL_TOKEN = '1zUeXUvr4LKymANBB_bLEqiP5GPr-Pha_OR6OOnV1o8Vy_rWhDoxKNIt';
2525
private const SELECTOR = '1zUeXUvr4LKymANBB_bLEqiP5GPr-Pha';
2626

27-
private static $randValue;
27+
private static HiddenString $randValue;
2828

2929
#[BeforeClass]
30-
public static function createRandomBytes()
30+
public static function createRandomBytes(): void
3131
{
32-
self::$randValue = new HiddenString(hex2bin('d7351e5d4bebe0b2b298034107f6cb12a88fe463ebf8f85afce47a38e9d5d68f15cbfad6843a3128d22d'), false, true);
32+
self::$randValue = new HiddenString((string) hex2bin('d7351e5d4bebe0b2b298034107f6cb12a88fe463ebf8f85afce47a38e9d5d68f15cbfad6843a3128d22d'), false, true);
3333
}
3434

3535
#[Test]
36-
public function it_validates_the_correct_length_less()
36+
public function it_validates_the_correct_length_less(): void
3737
{
3838
$this->expectException(\RuntimeException::class);
3939
$this->expectExceptionMessage('Invalid token-data provided, expected exactly 42 bytes.');
@@ -42,7 +42,7 @@ public function it_validates_the_correct_length_less()
4242
}
4343

4444
#[Test]
45-
public function it_validates_the_correct_length_more()
45+
public function it_validates_the_correct_length_more(): void
4646
{
4747
$this->expectException(\RuntimeException::class);
4848
$this->expectExceptionMessage('Invalid token-data provided, expected exactly 42 bytes.');
@@ -51,7 +51,7 @@ public function it_validates_the_correct_length_more()
5151
}
5252

5353
#[Test]
54-
public function it_validates_the_correct_length_from_string_less()
54+
public function it_validates_the_correct_length_from_string_less(): void
5555
{
5656
$this->expectException(\RuntimeException::class);
5757
$this->expectExceptionMessage('Invalid token provided.');
@@ -60,7 +60,7 @@ public function it_validates_the_correct_length_from_string_less()
6060
}
6161

6262
#[Test]
63-
public function it_validates_the_correct_length_from_string_more()
63+
public function it_validates_the_correct_length_from_string_more(): void
6464
{
6565
$this->expectException(\RuntimeException::class);
6666
$this->expectExceptionMessage('Invalid token provided.');
@@ -69,7 +69,7 @@ public function it_validates_the_correct_length_from_string_more()
6969
}
7070

7171
#[Test]
72-
public function it_creates_a_split_token_without_id()
72+
public function it_creates_a_split_token_without_id(): void
7373
{
7474
$splitToken = SplitToken::create(self::$randValue);
7575

@@ -78,7 +78,7 @@ public function it_creates_a_split_token_without_id()
7878
}
7979

8080
#[Test]
81-
public function it_creates_a_split_token_with_id()
81+
public function it_creates_a_split_token_with_id(): void
8282
{
8383
$splitToken = SplitToken::create($fullToken = self::$randValue);
8484

@@ -87,7 +87,7 @@ public function it_creates_a_split_token_with_id()
8787
}
8888

8989
#[Test]
90-
public function it_compares_two_split_tokens()
90+
public function it_compares_two_split_tokens(): void
9191
{
9292
$splitToken1 = SplitToken::create(self::$randValue);
9393

@@ -97,43 +97,46 @@ public function it_compares_two_split_tokens()
9797
}
9898

9999
#[Test]
100-
public function it_creates_a_split_token_with_custom_config()
100+
public function it_creates_a_split_token_with_custom_config(): void
101101
{
102102
$splitToken = SplitToken::create(self::$randValue, [
103103
'memory_cost' => 512,
104104
'time_cost' => 1,
105105
'threads' => 1,
106106
]);
107107

108-
self::assertMatchesRegularExpression('/^\$argon2[id]+\$v=19\$m=512,t=1,p=1/', $splitToken->toValueHolder()->verifierHash());
108+
self::assertNotNull($hash = $splitToken->toValueHolder()->verifierHash());
109+
self::assertMatchesRegularExpression('/^\$argon2id+\$v=19\$m=512,t=1,p=1/', $hash);
109110
}
110111

111112
#[Test]
112-
public function it_produces_a_split_token_value_holder()
113+
public function it_produces_a_split_token_value_holder(): void
113114
{
114115
$splitToken = SplitToken::create(self::$randValue);
115116

116117
$value = $splitToken->toValueHolder();
117118

118119
self::assertEquals($splitToken->selector(), $value->selector());
119-
self::assertStringStartsWith('$argon2i', $value->verifierHash());
120+
self::assertNotNull($hash = $splitToken->toValueHolder()->verifierHash());
121+
self::assertStringStartsWith('$argon2id', $hash);
120122
self::assertEquals([], $value->metadata());
121123
self::assertFalse($value->isExpired());
122124
self::assertFalse($value->isExpired(new \DateTimeImmutable('-5 minutes')));
123125
}
124126

125127
#[Test]
126-
public function it_produces_a_split_token_value_holder_with_metadata()
128+
public function it_produces_a_split_token_value_holder_with_metadata(): void
127129
{
128130
$splitToken = SplitToken::create(self::$randValue);
129131
$value = $splitToken->toValueHolder(['he' => 'now']);
130132

131-
self::assertStringStartsWith('$argon2i', $value->verifierHash());
133+
self::assertNotNull($hash = $splitToken->toValueHolder()->verifierHash());
134+
self::assertStringStartsWith('$argon2id', $hash);
132135
self::assertEquals(['he' => 'now'], $value->metadata());
133136
}
134137

135138
#[Test]
136-
public function it_produces_a_split_token_value_holder_with_expiration()
139+
public function it_produces_a_split_token_value_holder_with_expiration(): void
137140
{
138141
$date = new \DateTimeImmutable('+5 minutes');
139142
$splitToken = SplitToken::create($fullToken = self::$randValue)->expireAt($date);
@@ -146,7 +149,7 @@ public function it_produces_a_split_token_value_holder_with_expiration()
146149
}
147150

148151
#[Test]
149-
public function it_reconstructs_from_string()
152+
public function it_reconstructs_from_string(): void
150153
{
151154
$splitTokenReconstituted = SplitToken::fromString(self::FULL_TOKEN);
152155

@@ -155,7 +158,7 @@ public function it_reconstructs_from_string()
155158
}
156159

157160
#[Test]
158-
public function it_fails_when_creating_holder_with_string_constructed()
161+
public function it_fails_when_creating_holder_with_string_constructed(): void
159162
{
160163
$this->expectException(\RuntimeException::class);
161164
$this->expectExceptionMessage('toValueHolder() does not work with a SplitToken object when created with fromString().');
@@ -164,7 +167,7 @@ public function it_fails_when_creating_holder_with_string_constructed()
164167
}
165168

166169
#[Test]
167-
public function it_verifies_split_token()
170+
public function it_verifies_split_token(): void
168171
{
169172
// Stored.
170173
$splitTokenHolder = SplitToken::create(self::$randValue)->toValueHolder();
@@ -176,15 +179,15 @@ public function it_verifies_split_token()
176179
}
177180

178181
#[Test]
179-
public function it_verifies_split_token_from_string_and_no_current_token_set()
182+
public function it_verifies_split_token_from_string_and_no_current_token_set(): void
180183
{
181184
$fromString = SplitToken::fromString(self::FULL_TOKEN);
182185

183186
self::assertFalse($fromString->matches(null));
184187
}
185188

186189
#[Test]
187-
public function it_verifies_split_token_from_string_selector()
190+
public function it_verifies_split_token_from_string_selector(): void
188191
{
189192
// Stored.
190193
$splitTokenHolder = SplitToken::create(self::$randValue)->toValueHolder();
@@ -197,7 +200,7 @@ public function it_verifies_split_token_from_string_selector()
197200
}
198201

199202
#[Test]
200-
public function it_verifies_split_token_from_string_with_expiration()
203+
public function it_verifies_split_token_from_string_with_expiration(): void
201204
{
202205
// Stored.
203206
$splitTokenHolder = SplitToken::create(self::$randValue)

tests/FakeSplitTokenFactoryTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public function it_generates_with_default_expiration_date(): void
6868

6969
private static function assertExpirationEquals(string $expected, SplitToken $actual): void
7070
{
71+
self::assertNotNull($actual->getExpirationTime());
7172
self::assertSame($expected, $actual->getExpirationTime()->format('Y-m-d\TH:i:s'));
7273
}
7374

0 commit comments

Comments
 (0)