Skip to content

Commit

Permalink
Drop Psalm baseline (#457)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
spaze authored Jan 4, 2025
2 parents 380711c + 91ea5c8 commit a80820c
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 179 deletions.
3 changes: 1 addition & 2 deletions app/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "spaze/michalspacek.cz",
"type": "project",
"description": "Source code for my site",
"version": "1.3.3.7",
"license": "MIT",
"require": {
"php": "^8.3",
Expand Down Expand Up @@ -61,7 +60,7 @@
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-nette": "^2.0",
"psalm/phar": "^5.14",
"psalm/phar": "^5.26.1",
"roave/security-advisories": "dev-latest",
"shipmonk/composer-dependency-analyser": "^1.3",
"spaze/coding-standard": "^1.7.1",
Expand Down
2 changes: 1 addition & 1 deletion app/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 0 additions & 52 deletions app/psalm-baseline.xml

This file was deleted.

27 changes: 0 additions & 27 deletions app/psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
findUnusedCode="true"
findUnusedPsalmSuppress="true"
maxStringLength="2600"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="." />
Expand Down Expand Up @@ -50,17 +49,6 @@
<referencedMethod name="Nette\ComponentModel\Component::setParent" /> <!-- Used in tests -->
</errorLevel>
</InternalMethod>
<MissingFile>
<errorLevel type="suppress">
<file name="public/www.michalspacek.cz/app.php" /> <!-- Can't require maintenance.php but https://github.com/vimeo/psalm/issues/3886 -->
</errorLevel>
</MissingFile>
<MixedArgument>
<errorLevel type="suppress">
<referencedFunction name="/^Nette\\Forms\\Controls\\.*::addRule/" /> <!-- https://github.com/vimeo/psalm/issues/10870 -->
<referencedFunction name="/^Nette\\Forms\\Rules::addRule/" /> <!-- https://github.com/vimeo/psalm/issues/10870 -->
</errorLevel>
</MixedArgument>
<PossiblyUnusedMethod>
<errorLevel type="suppress">
<referencedMethod name="/.*::__construct$/" /> <!-- All services -->
Expand All @@ -70,25 +58,13 @@
<referencedMethod name="/.*::has.*/" /> <!-- Used mostly in templates -->
<referencedMethod name="/.*::jsonSerialize$/" /> <!-- Used by Nette\Application\UI\Presenter::sendJson() or by Nette\Utils\Json::encode() -->
<referencedMethod name="/.*::render$/" /> <!-- Classes that extend Nette\Application\UI\Control -->
<referencedMethod name="/.*Presenter::handle.*/" />
<referencedMethod name="/.*Presenter::inject.*/" />
<referencedMethod name="/.*Test::get.*/" /> <!-- Methods used in @dataProvider -->
<referencedMethod name="/.*Test::test.*/" />
<referencedMethod name="MichalSpacekCz\Application\Routing\RouterFactory::createRouter" /> <!-- Used in services.neon -->
<referencedMethod name="MichalSpacekCz\DateTime\DateTimeFormatter::localeMonth" /> <!-- Used in templates -->
<referencedMethod name="/^MichalSpacekCz\\Templating\\Filters::(staticUrl|staticImageUrl)$/" /> <!-- Used in templates -->
<referencedMethod name="/^MichalSpacekCz\\Test\\Http\\Request::(set|add).*/" /> <!-- Not used but keep them just in case -->
<referencedMethod name="/^MichalSpacekCz\\Test\\Http\\Response::(deleteHeader|sent).*/" /> <!-- Not used but keep them just in case -->
</errorLevel>
</PossiblyUnusedMethod>
<PossiblyUnusedProperty>
<errorLevel type="suppress">
<referencedProperty name="MichalSpacekCz\Articles\ArticleEdit::$summaryTexy" /> <!-- Not used because it's not possible to edit article edits, but keep for completeness -->
<referencedProperty name="MichalSpacekCz\Articles\ArticlePublishedElsewhere::$sourceName" /> <!-- Used in templates -->
<referencedProperty name="MichalSpacekCz\Articles\ArticlePublishedElsewhere::$sourceHref" /> <!-- Used in templates -->
<referencedProperty name="MichalSpacekCz\Articles\Blog\BlogPost::$originally" /> <!-- Used in templates -->
</errorLevel>
</PossiblyUnusedProperty>
<PossiblyUnusedReturnValue>
<errorLevel type="suppress">
<file name="src/Form/Controls/TrainingControlsFactory.php" /> <!-- Keep all add<Field>() methods similar and return even if not used -->
Expand All @@ -98,8 +74,6 @@
</PossiblyUnusedReturnValue>
<PropertyNotSetInConstructor>
<errorLevel type="suppress">
<referencedProperty name="Nette\Application\UI\Presenter::$invalidLinkMode" />
<referencedProperty name="Nette\Application\UI\Control::$snippetMode" />
<referencedProperty name="Nette\Forms\Form::$httpRequest" /> <!-- Marked as @internal -->
</errorLevel>
</PropertyNotSetInConstructor>
Expand All @@ -111,7 +85,6 @@
<UnusedClass>
<errorLevel type="suppress">
<referencedClass name="*Presenter" />
<referencedClass name="MichalSpacekCz\CompanyInfo\CompanyRegister*" /> <!-- An array of these is passed to MichalSpacekCz\CompanyInfo\CompanyInfo::__construct() by the DIC -->
<referencedClass name="MichalSpacekCz\Formatter\Placeholders\*" /> <!-- An array of these is passed to MichalSpacekCz\Formatter\TexyFormatter::__construct() by the DIC -->
<referencedClass name="MichalSpacekCz\Test\*\Null*" /> <!-- Used in tests.neon -->
<referencedClass name="MichalSpacekCz\Tls\CertificateMonitor" /> <!-- Used in bin/certmonitor.php but can't analyze bin because https://github.com/vimeo/psalm/issues/10143 -->
Expand Down
7 changes: 4 additions & 3 deletions app/src/EasterEgg/WinterIsComing.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ class WinterIsComing
public function ruleEmail(): callable
{
return function (TextInput $input) {
$value = $input->getValue();
if (
is_string($input->getValue())
is_string($value)
&& (
Arrays::contains(self::EMAILS, $input->getValue())
|| Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $input->getValue())
Arrays::contains(self::EMAILS, $value)
|| Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $value)
)
) {
$this->sendSyntaxError($input);
Expand Down
1 change: 1 addition & 0 deletions app/src/Formatter/TexyFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ private function replace(string $key, Texy $texy, callable $callback): Html

$result = Regex::replaceCallbackStrictGroups(
'~\*\*([^:]+):([^*]+)\*\*~',
/** @param array<int, string> $matches */
function (array $matches) use ($replacements): string {
return (isset($replacements[$matches[1]]) ? $replacements[$matches[1]]($matches[2]) : '');
},
Expand Down
68 changes: 30 additions & 38 deletions app/src/Pulse/Passwords/PasswordsSorting.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,46 +45,38 @@ public function __construct()

public function sort(StorageRegistry $storages, string $sort): StorageRegistry
{
switch ($sort) {
case self::RATING_A_F:
case self::RATING_F_A:
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
$result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
$sorter = match ($sort) {
self::RATING_A_F, self::RATING_F_A => function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
$result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
if ($result === 0) {
$result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
if ($result === 0) {
$result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
if ($result === 0) {
$subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
$subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
$result = $subKeyA <=> $subKeyB;
}
$subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
$subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
$result = $subKeyA <=> $subKeyB;
}
return $result;
});
};
break;
case self::NEWEST_DISCLOSURES_FIRST:
case self::NEWEST_DISCLOSURES_LAST:
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
return $sort === self::NEWEST_DISCLOSURES_LAST
? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
: $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
});
};
break;
case self::NEWLY_ADDED_FIRST:
case self::NEWLY_ADDED_LAST:
$sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
$addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
$addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
});
};
break;
}
if (isset($sorter)) {
}
return $result;
});
},
self::NEWEST_DISCLOSURES_FIRST, self::NEWEST_DISCLOSURES_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
return $sort === self::NEWEST_DISCLOSURES_LAST
? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
: $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
});
},
self::NEWLY_ADDED_FIRST, self::NEWLY_ADDED_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
$addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
$addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
});
},
default => null,
};
if ($sorter !== null) {
$storages->sortStorages($sorter);
}
return $storages;
Expand Down
6 changes: 4 additions & 2 deletions app/src/Talks/Slides/TalkSlides.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ private function updateSlides(int $talkId, TalkSlideCollection $originalSlides,
assert(is_string($slide->speakerNotes));
$width = self::SLIDE_MAX_WIDTH;
$height = self::SLIDE_MAX_HEIGHT;
$slideFilename = $slide->filename;
$slideFilenameAlternative = $slide->filenameAlternative;

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

try {
$this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slide->filename ?? '', $replaceAlternative ?? $slide->filenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
$this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slideFilename ?? '', $replaceAlternative ?? $slideFilenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
} catch (UniqueConstraintViolationException $e) {
throw new DuplicatedSlideException($slide->number, previous: $e);
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/Tls/CertificateGatherer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ public function fetchCertificates(string $hostname, bool $includeIpv6): array
$certificates = [];
$records = $this->dnsResolver->getRecords($hostname, $includeIpv6 ? DNS_A | DNS_AAAA : DNS_A);
foreach ($records as $record) {
$ipAddress = null;
if ($record->getIpv6() !== null) {
$ipAddress = "[{$record->getIpv6()}]";
} elseif ($record->getIp() !== null) {
$ipAddress = $record->getIp();
}
if (!isset($ipAddress)) {
if ($ipAddress === null) {
throw new DnsGetRecordException("No IPv4/v6 address for {$hostname}");
}
$certificates[$ipAddress] = $this->fetchCertificate($hostname, $ipAddress);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);

namespace MichalSpacekCz\Training\Exceptions;

use Throwable;

class TrainingReviewRankingInvalidException extends TrainingException
{

public function __construct(int $id, int $rating, ?Throwable $previous = null)
{
parent::__construct("The rating of the training review id '{$id}' is invalid: '{$rating}'", previous: $previous);
}

}
15 changes: 12 additions & 3 deletions app/src/Training/Reviews/TrainingReviews.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use MichalSpacekCz\DateTime\DateTimeFactory;
use MichalSpacekCz\Formatter\TexyFormatter;
use MichalSpacekCz\Training\Exceptions\TrainingReviewNotFoundException;
use MichalSpacekCz\Training\Exceptions\TrainingReviewRankingInvalidException;
use Nette\Database\Explorer;
use Nette\Database\Row;

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

/**
* @return list<TrainingReview>
* @throws TrainingReviewRankingInvalidException
*/
public function getVisibleReviews(int $id, ?int $limit = null): array
{
Expand Down Expand Up @@ -62,6 +64,7 @@ public function getVisibleReviews(int $id, ?int $limit = null): array
* Get all reviews including hidden by training id.
*
* @return list<TrainingReview>
* @throws TrainingReviewRankingInvalidException
*/
public function getAllReviews(int $id): array
{
Expand Down Expand Up @@ -95,6 +98,7 @@ public function getAllReviews(int $id): array

/**
* @throws TrainingReviewNotFoundException
* @throws TrainingReviewRankingInvalidException
*/
public function getReview(int $reviewId): TrainingReview
{
Expand Down Expand Up @@ -127,6 +131,7 @@ public function getReview(int $reviewId): TrainingReview

/**
* @return list<TrainingReview>
* @throws TrainingReviewRankingInvalidException
*/
public function getReviewsByDateId(int $dateId): array
{
Expand Down Expand Up @@ -198,20 +203,24 @@ public function addReview(int $dateId, string $name, string $company, ?string $j
}


/**
* @throws TrainingReviewRankingInvalidException
*/
private function createFromDatabaseRow(Row $row): TrainingReview
{
assert(is_int($row->id));
assert(is_string($row->name));
assert(is_string($row->company));
assert($row->jobTitle === null || is_string($row->jobTitle));
assert(is_string($row->review));
assert(is_string($row->reviewTexy));
assert($row->href === null || is_string($row->href));
assert(is_int($row->hidden));
assert($row->ranking === null || is_int($row->ranking) && $row->ranking > 0);
assert($row->ranking === null || is_int($row->ranking));
assert($row->note === null || is_string($row->note));
assert(is_int($row->dateId));

if ($row->ranking !== null && $row->ranking <= 0) {
throw new TrainingReviewRankingInvalidException($row->id, $row->ranking);
}
return new TrainingReview(
$row->id,
$row->name,
Expand Down
Loading

0 comments on commit a80820c

Please # to comment.