Skip to content

Commit a80820c

Browse files
authored
Drop Psalm baseline (#457)
By adding some checks, or asserts, or by refactoring the code. Also un-suppress some errors that are not reported anymore, and remove version number from `composer.json` so that `composer validate` doesn't complain anymore.
2 parents 380711c + 91ea5c8 commit a80820c

File tree

15 files changed

+347
-179
lines changed

15 files changed

+347
-179
lines changed

app/composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"name": "spaze/michalspacek.cz",
33
"type": "project",
44
"description": "Source code for my site",
5-
"version": "1.3.3.7",
65
"license": "MIT",
76
"require": {
87
"php": "^8.3",
@@ -61,7 +60,7 @@
6160
"phpstan/phpstan": "^2.0",
6261
"phpstan/phpstan-deprecation-rules": "^2.0",
6362
"phpstan/phpstan-nette": "^2.0",
64-
"psalm/phar": "^5.14",
63+
"psalm/phar": "^5.26.1",
6564
"roave/security-advisories": "dev-latest",
6665
"shipmonk/composer-dependency-analyser": "^1.3",
6766
"spaze/coding-standard": "^1.7.1",

app/composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/psalm-baseline.xml

Lines changed: 0 additions & 52 deletions
This file was deleted.

app/psalm.xml

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
findUnusedCode="true"
1010
findUnusedPsalmSuppress="true"
1111
maxStringLength="2600"
12-
errorBaseline="psalm-baseline.xml"
1312
>
1413
<projectFiles>
1514
<directory name="." />
@@ -50,17 +49,6 @@
5049
<referencedMethod name="Nette\ComponentModel\Component::setParent" /> <!-- Used in tests -->
5150
</errorLevel>
5251
</InternalMethod>
53-
<MissingFile>
54-
<errorLevel type="suppress">
55-
<file name="public/www.michalspacek.cz/app.php" /> <!-- Can't require maintenance.php but https://github.com/vimeo/psalm/issues/3886 -->
56-
</errorLevel>
57-
</MissingFile>
58-
<MixedArgument>
59-
<errorLevel type="suppress">
60-
<referencedFunction name="/^Nette\\Forms\\Controls\\.*::addRule/" /> <!-- https://github.com/vimeo/psalm/issues/10870 -->
61-
<referencedFunction name="/^Nette\\Forms\\Rules::addRule/" /> <!-- https://github.com/vimeo/psalm/issues/10870 -->
62-
</errorLevel>
63-
</MixedArgument>
6452
<PossiblyUnusedMethod>
6553
<errorLevel type="suppress">
6654
<referencedMethod name="/.*::__construct$/" /> <!-- All services -->
@@ -70,25 +58,13 @@
7058
<referencedMethod name="/.*::has.*/" /> <!-- Used mostly in templates -->
7159
<referencedMethod name="/.*::jsonSerialize$/" /> <!-- Used by Nette\Application\UI\Presenter::sendJson() or by Nette\Utils\Json::encode() -->
7260
<referencedMethod name="/.*::render$/" /> <!-- Classes that extend Nette\Application\UI\Control -->
73-
<referencedMethod name="/.*Presenter::handle.*/" />
7461
<referencedMethod name="/.*Presenter::inject.*/" />
75-
<referencedMethod name="/.*Test::get.*/" /> <!-- Methods used in @dataProvider -->
7662
<referencedMethod name="/.*Test::test.*/" />
7763
<referencedMethod name="MichalSpacekCz\Application\Routing\RouterFactory::createRouter" /> <!-- Used in services.neon -->
78-
<referencedMethod name="MichalSpacekCz\DateTime\DateTimeFormatter::localeMonth" /> <!-- Used in templates -->
79-
<referencedMethod name="/^MichalSpacekCz\\Templating\\Filters::(staticUrl|staticImageUrl)$/" /> <!-- Used in templates -->
8064
<referencedMethod name="/^MichalSpacekCz\\Test\\Http\\Request::(set|add).*/" /> <!-- Not used but keep them just in case -->
8165
<referencedMethod name="/^MichalSpacekCz\\Test\\Http\\Response::(deleteHeader|sent).*/" /> <!-- Not used but keep them just in case -->
8266
</errorLevel>
8367
</PossiblyUnusedMethod>
84-
<PossiblyUnusedProperty>
85-
<errorLevel type="suppress">
86-
<referencedProperty name="MichalSpacekCz\Articles\ArticleEdit::$summaryTexy" /> <!-- Not used because it's not possible to edit article edits, but keep for completeness -->
87-
<referencedProperty name="MichalSpacekCz\Articles\ArticlePublishedElsewhere::$sourceName" /> <!-- Used in templates -->
88-
<referencedProperty name="MichalSpacekCz\Articles\ArticlePublishedElsewhere::$sourceHref" /> <!-- Used in templates -->
89-
<referencedProperty name="MichalSpacekCz\Articles\Blog\BlogPost::$originally" /> <!-- Used in templates -->
90-
</errorLevel>
91-
</PossiblyUnusedProperty>
9268
<PossiblyUnusedReturnValue>
9369
<errorLevel type="suppress">
9470
<file name="src/Form/Controls/TrainingControlsFactory.php" /> <!-- Keep all add<Field>() methods similar and return even if not used -->
@@ -98,8 +74,6 @@
9874
</PossiblyUnusedReturnValue>
9975
<PropertyNotSetInConstructor>
10076
<errorLevel type="suppress">
101-
<referencedProperty name="Nette\Application\UI\Presenter::$invalidLinkMode" />
102-
<referencedProperty name="Nette\Application\UI\Control::$snippetMode" />
10377
<referencedProperty name="Nette\Forms\Form::$httpRequest" /> <!-- Marked as @internal -->
10478
</errorLevel>
10579
</PropertyNotSetInConstructor>
@@ -111,7 +85,6 @@
11185
<UnusedClass>
11286
<errorLevel type="suppress">
11387
<referencedClass name="*Presenter" />
114-
<referencedClass name="MichalSpacekCz\CompanyInfo\CompanyRegister*" /> <!-- An array of these is passed to MichalSpacekCz\CompanyInfo\CompanyInfo::__construct() by the DIC -->
11588
<referencedClass name="MichalSpacekCz\Formatter\Placeholders\*" /> <!-- An array of these is passed to MichalSpacekCz\Formatter\TexyFormatter::__construct() by the DIC -->
11689
<referencedClass name="MichalSpacekCz\Test\*\Null*" /> <!-- Used in tests.neon -->
11790
<referencedClass name="MichalSpacekCz\Tls\CertificateMonitor" /> <!-- Used in bin/certmonitor.php but can't analyze bin because https://github.com/vimeo/psalm/issues/10143 -->

app/src/EasterEgg/WinterIsComing.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ class WinterIsComing
3838
public function ruleEmail(): callable
3939
{
4040
return function (TextInput $input) {
41+
$value = $input->getValue();
4142
if (
42-
is_string($input->getValue())
43+
is_string($value)
4344
&& (
44-
Arrays::contains(self::EMAILS, $input->getValue())
45-
|| Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $input->getValue())
45+
Arrays::contains(self::EMAILS, $value)
46+
|| Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $value)
4647
)
4748
) {
4849
$this->sendSyntaxError($input);

app/src/Formatter/TexyFormatter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ private function replace(string $key, Texy $texy, callable $callback): Html
190190

191191
$result = Regex::replaceCallbackStrictGroups(
192192
'~\*\*([^:]+):([^*]+)\*\*~',
193+
/** @param array<int, string> $matches */
193194
function (array $matches) use ($replacements): string {
194195
return (isset($replacements[$matches[1]]) ? $replacements[$matches[1]]($matches[2]) : '');
195196
},

app/src/Pulse/Passwords/PasswordsSorting.php

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -45,46 +45,38 @@ public function __construct()
4545

4646
public function sort(StorageRegistry $storages, string $sort): StorageRegistry
4747
{
48-
switch ($sort) {
49-
case self::RATING_A_F:
50-
case self::RATING_F_A:
51-
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
52-
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
53-
$result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
48+
$sorter = match ($sort) {
49+
self::RATING_A_F, self::RATING_F_A => function (Storage $a, Storage $b) use ($storages, $sort): int {
50+
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
51+
$result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
52+
if ($result === 0) {
53+
$result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
5454
if ($result === 0) {
55-
$result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
56-
if ($result === 0) {
57-
$subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
58-
$subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
59-
$result = $subKeyA <=> $subKeyB;
60-
}
55+
$subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
56+
$subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
57+
$result = $subKeyA <=> $subKeyB;
6158
}
62-
return $result;
63-
});
64-
};
65-
break;
66-
case self::NEWEST_DISCLOSURES_FIRST:
67-
case self::NEWEST_DISCLOSURES_LAST:
68-
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
69-
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
70-
return $sort === self::NEWEST_DISCLOSURES_LAST
71-
? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
72-
: $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
73-
});
74-
};
75-
break;
76-
case self::NEWLY_ADDED_FIRST:
77-
case self::NEWLY_ADDED_LAST:
78-
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
79-
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
80-
$addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
81-
$addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
82-
return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
83-
});
84-
};
85-
break;
86-
}
87-
if (isset($sorter)) {
59+
}
60+
return $result;
61+
});
62+
},
63+
self::NEWEST_DISCLOSURES_FIRST, self::NEWEST_DISCLOSURES_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
64+
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
65+
return $sort === self::NEWEST_DISCLOSURES_LAST
66+
? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
67+
: $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
68+
});
69+
},
70+
self::NEWLY_ADDED_FIRST, self::NEWLY_ADDED_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
71+
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
72+
$addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
73+
$addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
74+
return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
75+
});
76+
},
77+
default => null,
78+
};
79+
if ($sorter !== null) {
8880
$storages->sortStorages($sorter);
8981
}
9082
return $storages;

app/src/Talks/Slides/TalkSlides.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ private function updateSlides(int $talkId, TalkSlideCollection $originalSlides,
267267
assert(is_string($slide->speakerNotes));
268268
$width = self::SLIDE_MAX_WIDTH;
269269
$height = self::SLIDE_MAX_HEIGHT;
270+
$slideFilename = $slide->filename;
271+
$slideFilenameAlternative = $slide->filenameAlternative;
270272

271273
if (isset($slide->replace, $slide->replaceAlternative)) {
272274
$replace = $this->replaceSlideImage($talkId, $slide->replace, $this->supportedImageFileFormats->getMainExtensionByContentType(...), $removeFiles, $originalSlides->getByNumber($slide->number)->getFilename(), $width, $height);
@@ -279,11 +281,11 @@ private function updateSlides(int $talkId, TalkSlideCollection $originalSlides,
279281
}
280282
}
281283
} else {
282-
$replace = $replaceAlternative = $slide->filename = $slide->filenameAlternative = null;
284+
$replace = $replaceAlternative = $slideFilename = $slideFilenameAlternative = null;
283285
}
284286

285287
try {
286-
$this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slide->filename ?? '', $replaceAlternative ?? $slide->filenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
288+
$this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slideFilename ?? '', $replaceAlternative ?? $slideFilenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
287289
} catch (UniqueConstraintViolationException $e) {
288290
throw new DuplicatedSlideException($slide->number, previous: $e);
289291
}

app/src/Tls/CertificateGatherer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ public function fetchCertificates(string $hostname, bool $includeIpv6): array
4444
$certificates = [];
4545
$records = $this->dnsResolver->getRecords($hostname, $includeIpv6 ? DNS_A | DNS_AAAA : DNS_A);
4646
foreach ($records as $record) {
47+
$ipAddress = null;
4748
if ($record->getIpv6() !== null) {
4849
$ipAddress = "[{$record->getIpv6()}]";
4950
} elseif ($record->getIp() !== null) {
5051
$ipAddress = $record->getIp();
5152
}
52-
if (!isset($ipAddress)) {
53+
if ($ipAddress === null) {
5354
throw new DnsGetRecordException("No IPv4/v6 address for {$hostname}");
5455
}
5556
$certificates[$ipAddress] = $this->fetchCertificate($hostname, $ipAddress);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace MichalSpacekCz\Training\Exceptions;
5+
6+
use Throwable;
7+
8+
class TrainingReviewRankingInvalidException extends TrainingException
9+
{
10+
11+
public function __construct(int $id, int $rating, ?Throwable $previous = null)
12+
{
13+
parent::__construct("The rating of the training review id '{$id}' is invalid: '{$rating}'", previous: $previous);
14+
}
15+
16+
}

app/src/Training/Reviews/TrainingReviews.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use MichalSpacekCz\DateTime\DateTimeFactory;
88
use MichalSpacekCz\Formatter\TexyFormatter;
99
use MichalSpacekCz\Training\Exceptions\TrainingReviewNotFoundException;
10+
use MichalSpacekCz\Training\Exceptions\TrainingReviewRankingInvalidException;
1011
use Nette\Database\Explorer;
1112
use Nette\Database\Row;
1213

@@ -24,6 +25,7 @@ public function __construct(
2425

2526
/**
2627
* @return list<TrainingReview>
28+
* @throws TrainingReviewRankingInvalidException
2729
*/
2830
public function getVisibleReviews(int $id, ?int $limit = null): array
2931
{
@@ -62,6 +64,7 @@ public function getVisibleReviews(int $id, ?int $limit = null): array
6264
* Get all reviews including hidden by training id.
6365
*
6466
* @return list<TrainingReview>
67+
* @throws TrainingReviewRankingInvalidException
6568
*/
6669
public function getAllReviews(int $id): array
6770
{
@@ -95,6 +98,7 @@ public function getAllReviews(int $id): array
9598

9699
/**
97100
* @throws TrainingReviewNotFoundException
101+
* @throws TrainingReviewRankingInvalidException
98102
*/
99103
public function getReview(int $reviewId): TrainingReview
100104
{
@@ -127,6 +131,7 @@ public function getReview(int $reviewId): TrainingReview
127131

128132
/**
129133
* @return list<TrainingReview>
134+
* @throws TrainingReviewRankingInvalidException
130135
*/
131136
public function getReviewsByDateId(int $dateId): array
132137
{
@@ -198,20 +203,24 @@ public function addReview(int $dateId, string $name, string $company, ?string $j
198203
}
199204

200205

206+
/**
207+
* @throws TrainingReviewRankingInvalidException
208+
*/
201209
private function createFromDatabaseRow(Row $row): TrainingReview
202210
{
203211
assert(is_int($row->id));
204212
assert(is_string($row->name));
205213
assert(is_string($row->company));
206214
assert($row->jobTitle === null || is_string($row->jobTitle));
207215
assert(is_string($row->review));
208-
assert(is_string($row->reviewTexy));
209216
assert($row->href === null || is_string($row->href));
210217
assert(is_int($row->hidden));
211-
assert($row->ranking === null || is_int($row->ranking) && $row->ranking > 0);
218+
assert($row->ranking === null || is_int($row->ranking));
212219
assert($row->note === null || is_string($row->note));
213220
assert(is_int($row->dateId));
214-
221+
if ($row->ranking !== null && $row->ranking <= 0) {
222+
throw new TrainingReviewRankingInvalidException($row->id, $row->ranking);
223+
}
215224
return new TrainingReview(
216225
$row->id,
217226
$row->name,

0 commit comments

Comments
 (0)