From 4ba6a96fafec5f68ac20405ec55c6d3cb4537e7a Mon Sep 17 00:00:00 2001 From: "Garrett W." Date: Wed, 1 Jan 2025 16:42:41 -0600 Subject: [PATCH] PHPStan (+PHPUnit) corrections, pt 1 (#1341) --- .php-cs-fixer.dist.php | 3 ++ src/ActivityPub/ActorHandle.php | 6 +-- src/ActivityPub/JsonRd.php | 47 ++++++++++++++----- src/ActivityPub/JsonRdLink.php | 37 ++++++++++----- .../FavouriteResolver.php | 1 + src/ArgumentValueResolver/ReportResolver.php | 1 + src/ArgumentValueResolver/VotableResolver.php | 1 + src/Command/AwesomeBot/AwesomeBotEntries.php | 3 +- src/Command/AwesomeBot/AwesomeBotFixtures.php | 14 ++++-- src/Command/AwesomeBot/AwesomeBotMagazine.php | 3 ++ src/Command/MoveEntriesByTagCommand.php | 16 +++++-- src/Command/MovePostsByTagCommand.php | 18 +++++-- src/Command/RemoveDeadMessagesCommand.php | 2 +- src/Command/RemoveDuplicatesCommand.php | 4 +- src/Command/RemoveFailedMessagesCommand.php | 2 +- src/Command/Update/ApKeysUpdateCommand.php | 8 ++-- .../Update/Async/ImageBlurhashHandler.php | 7 ++- .../Update/Async/NoteVisibilityHandler.php | 7 +-- .../Update/Async/NoteVisibilityMessage.php | 5 ++ .../Update/PostCommentRootUpdateCommand.php | 2 +- src/Command/Update/TagsUpdateCommand.php | 3 ++ .../Update/UserLastActiveUpdateCommand.php | 2 + .../ActivityPub/NodeInfoController.php | 2 + .../Admin/AdminDashboardController.php | 3 +- .../Admin/AdminFederationController.php | 3 +- src/Controller/Admin/AdminUsersController.php | 9 ++-- src/Controller/Api/BaseApi.php | 11 +++-- src/Controller/Api/Domain/DomainBaseApi.php | 4 +- src/Controller/Api/Entry/EntriesBaseApi.php | 2 +- src/DTO/MagazineLogResponseDto.php | 3 +- .../ActivityPub/Inbox/ActivityMessage.php | 2 +- src/Repository/DomainRepository.php | 13 +++-- src/Repository/EntryCommentRepository.php | 3 +- src/Repository/EntryRepository.php | 6 ++- src/Service/SearchManager.php | 2 +- tests/Unit/ActivityPub/ActorHandleTest.php | 9 ++-- .../ActivityPub/SignatureValidatorTest.php | 9 ++-- tests/Unit/Service/TagExtractorTest.php | 5 +- tests/Unit/Utils/SluggerTest.php | 5 +- 39 files changed, 182 insertions(+), 101 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 4bea1f5aa..9724ea378 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -21,6 +21,9 @@ 'declare_strict_types' => true, 'strict_comparison' => true, 'native_function_invocation' => true, + 'phpdoc_to_comment' => [ + 'ignored_tags' => ['var'] + ] ]) ->setRiskyAllowed(true) ->setFinder($finder) diff --git a/src/ActivityPub/ActorHandle.php b/src/ActivityPub/ActorHandle.php index 486eeac18..59c7ceb4f 100644 --- a/src/ActivityPub/ActorHandle.php +++ b/src/ActivityPub/ActorHandle.php @@ -37,7 +37,7 @@ public static function parse(string $handle): ?static return null; } - public static function isHandle(string $handle) + public static function isHandle(string $handle): bool { if (preg_match(static::HANDLE_PATTERN, $handle, $matches)) { return !empty($matches['name']) && !empty($matches['host']); @@ -58,7 +58,7 @@ public function getPortString(): string } /** @param int|string|null $port port as either plain int or string formatted like ':9000' */ - public function setPort(int|string|null $port) + public function setPort(int|string|null $port): static { if (\is_string($port)) { $this->port = \intval(ltrim($port, ':')); @@ -76,7 +76,7 @@ public function getDomain(): string } /** @param ?string $domain the domain in the format `host[:port]` to set both handle's host and port */ - public function setDomain(?string $domain) + public function setDomain(?string $domain): static { $url = parse_url($domain ?? ''); diff --git a/src/ActivityPub/JsonRd.php b/src/ActivityPub/JsonRd.php index 3bf9a4975..1bc93ada2 100644 --- a/src/ActivityPub/JsonRd.php +++ b/src/ActivityPub/JsonRd.php @@ -53,7 +53,7 @@ class JsonRd * identify the same entity as the "subject" URI. * The "aliases" array is OPTIONAL in the JRD. * - * @var array[string] + * @var string[] */ protected $aliases = []; @@ -68,7 +68,7 @@ class JsonRd * * The "properties" member is OPTIONAL in the JRD. * - * @var array[string=>string] + * @var array */ protected $properties = []; @@ -76,18 +76,18 @@ class JsonRd * The "links" array has any number of member objects, each of which * represents a link [4]. * - * @var array[JsonRdLink] + * @var JsonRdLink[] */ protected $links = []; - public function addAlias(string $uri): JsonRd + public function addAlias(string $uri): static { array_push($this->aliases, $uri); return $this; } - public function removeAlias(string $uri): JsonRd + public function removeAlias(string $uri): static { $key = array_search($uri, $this->aliases); if (false !== $key) { @@ -97,14 +97,14 @@ public function removeAlias(string $uri): JsonRd return $this; } - public function addProperty(string $uri, ?string $value = null): JsonRd + public function addProperty(string $uri, ?string $value = null): static { $this->properties[$uri] = $value; return $this; } - public function removeProperty(string $uri): JsonRd + public function removeProperty(string $uri): static { if (!\array_key_exists($uri, $this->properties)) { return $this; @@ -114,14 +114,14 @@ public function removeProperty(string $uri): JsonRd return $this; } - public function addLink(JsonRdLink $link): JsonRd + public function addLink(JsonRdLink $link): static { array_push($this->links, $link); return $this; } - public function removeLink(JsonRdLink $link): JsonRd + public function removeLink(JsonRdLink $link): static { $serialized_link = serialize($link); foreach ($this->links as $key => $_link) { @@ -140,6 +140,9 @@ public function toJSON(): string return json_encode($this->toArray()); } + /** + * @return array + */ public function toArray(): array { $data = []; @@ -163,43 +166,61 @@ public function getSubject() return $this->subject; } - public function setSubject(string $subject): JsonRd + public function setSubject(string $subject): static { $this->subject = $subject; return $this; } + /** + * @return string[] + */ public function getAliases(): array { return $this->aliases; } - protected function setAliases(array $aliases): JsonRd + /** + * @param string[] $aliases + */ + protected function setAliases(array $aliases): static { $this->aliases = $aliases; return $this; } + /** + * @return JsonRdLink[] + */ public function getLinks(): array { return $this->links; } - protected function setLinks(array $links): JsonRd + /** + * @param JsonRdLink[] $links + */ + protected function setLinks(array $links): static { $this->links = $links; return $this; } + /** + * @return array + */ public function getProperties(): array { return $this->properties; } - protected function setProperties(array $properties): JsonRd + /** + * @param array $properties + */ + protected function setProperties(array $properties): static { $this->properties = $properties; diff --git a/src/ActivityPub/JsonRdLink.php b/src/ActivityPub/JsonRdLink.php index 6a6115d89..bec467658 100644 --- a/src/ActivityPub/JsonRdLink.php +++ b/src/ActivityPub/JsonRdLink.php @@ -208,7 +208,7 @@ class JsonRdLink * * The "titles" member is OPTIONAL in the link relation object. * - * @var array[string=>string] + * @var array */ protected $titles = []; @@ -223,11 +223,11 @@ class JsonRdLink * * The "properties" member is OPTIONAL in the link relation object. * - * @var array[string=>string] + * @var array */ protected $properties = []; - public function addTitle(string $locale, string $value): JsonRdLink + public function addTitle(string $locale, string $value): static { if (!\array_key_exists($locale, $this->titles)) { $this->titles[$locale] = $value; @@ -236,7 +236,7 @@ public function addTitle(string $locale, string $value): JsonRdLink return $this; } - public function removeTitle(string $locale): JsonRdLink + public function removeTitle(string $locale): static { if (!\array_key_exists($locale, $this->titles)) { return $this; @@ -246,14 +246,14 @@ public function removeTitle(string $locale): JsonRdLink return $this; } - public function addProperty(string $url, string $value): JsonRdLink + public function addProperty(string $url, string $value): static { $this->properties[$url] = $value; return $this; } - public function removeProperty(string $url): JsonRdLink + public function removeProperty(string $url): static { if (!\array_key_exists($url, $this->properties)) { return $this; @@ -263,6 +263,9 @@ public function removeProperty(string $url): JsonRdLink return $this; } + /** + * @return array + */ public function toArray(): array { $data = []; @@ -284,7 +287,7 @@ public function getRel(): string /** * @throws \Exception */ - public function setRel(string $relation): JsonRdLink + public function setRel(string $relation): static { if (\in_array($relation, self::REGISTERED_RELATION_TYPES)) { $this->rel = $relation; @@ -308,7 +311,7 @@ public function getHref(): ?string /** * @todo we need to write for url validation for $href argument. */ - public function setHref(string $href): JsonRdLink + public function setHref(string $href): static { $this->href = $href; @@ -320,31 +323,43 @@ public function getType(): ?string return $this->type; } - public function setType(string $type): JsonRdLink + public function setType(string $type): static { $this->type = $type; return $this; } + /** + * @return array + */ public function getTitles(): array { return $this->titles; } - protected function setTitles(array $titles): JsonRdLink + /** + * @param array $titles + */ + protected function setTitles(array $titles): static { $this->titles = $titles; return $this; } + /** + * @return array + */ public function getProperties(): array { return $this->properties; } - protected function setProperties(array $properties): JsonRdLink + /** + * @param array $properties + */ + protected function setProperties(array $properties): static { $this->properties = $properties; diff --git a/src/ArgumentValueResolver/FavouriteResolver.php b/src/ArgumentValueResolver/FavouriteResolver.php index 0963e12c5..70c5babef 100644 --- a/src/ArgumentValueResolver/FavouriteResolver.php +++ b/src/ArgumentValueResolver/FavouriteResolver.php @@ -26,6 +26,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): \Generato ) { ['id' => $id, 'entityClass' => $entityClass] = $request->attributes->all(); + /** @var class-string $entityClass */ yield $this->entityManager->find($entityClass, $id); } } diff --git a/src/ArgumentValueResolver/ReportResolver.php b/src/ArgumentValueResolver/ReportResolver.php index d06894462..d582c9c51 100644 --- a/src/ArgumentValueResolver/ReportResolver.php +++ b/src/ArgumentValueResolver/ReportResolver.php @@ -26,6 +26,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): \Generato ) { ['id' => $id, 'entityClass' => $entityClass] = $request->attributes->all(); + /** @var class-string $entityClass */ yield $this->entityManager->find($entityClass, $id); } } diff --git a/src/ArgumentValueResolver/VotableResolver.php b/src/ArgumentValueResolver/VotableResolver.php index 761caa980..baa8d323c 100644 --- a/src/ArgumentValueResolver/VotableResolver.php +++ b/src/ArgumentValueResolver/VotableResolver.php @@ -26,6 +26,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): \Generato ) { ['id' => $id, 'entityClass' => $entityClass] = $request->attributes->all(); + /** @var class-string $entityClass */ yield $this->entityManager->find($entityClass, $id); } } diff --git a/src/Command/AwesomeBot/AwesomeBotEntries.php b/src/Command/AwesomeBot/AwesomeBotEntries.php index 6553df953..331e250a0 100644 --- a/src/Command/AwesomeBot/AwesomeBotEntries.php +++ b/src/Command/AwesomeBot/AwesomeBotEntries.php @@ -10,7 +10,6 @@ use App\Repository\UserRepository; use App\Service\EntryManager; use Doctrine\Common\Collections\ArrayCollection; -use DOMElement; use Symfony\Component\BrowserKit\HttpBrowser; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -80,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ('ul' === $elem->nodeName) { foreach ($elem->childNodes as $li) { /** - * @var $li DOMElement + * @var \DOMElement $li */ if ('li' !== $li->nodeName) { continue; diff --git a/src/Command/AwesomeBot/AwesomeBotFixtures.php b/src/Command/AwesomeBot/AwesomeBotFixtures.php index 86cf4e402..4ca0293ad 100644 --- a/src/Command/AwesomeBot/AwesomeBotFixtures.php +++ b/src/Command/AwesomeBot/AwesomeBotFixtures.php @@ -10,7 +10,6 @@ use App\Repository\UserRepository; use App\Service\EntryManager; use Doctrine\Common\Collections\ArrayCollection; -use DOMElement; use Symfony\Component\BrowserKit\HttpBrowser; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -44,11 +43,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); + /** @var array[] */ $result = []; foreach ($this->getEntries() as $entry) { if ($input->getOption('prepare')) { - $this->preapreMagazines($output, $entry); + $this->prepareMagazines($output, $entry); continue; } @@ -81,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ('ul' === $elem->nodeName) { foreach ($elem->childNodes as $li) { /** - * @var $li DOMElement + * @var \DOMElement $li */ if ('li' !== $li->nodeName) { continue; @@ -134,6 +134,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } + /** + * @return array[] + */ private function getEntries(): array { return [ @@ -329,7 +332,10 @@ private function getEntries(): array ]; } - private function preapreMagazines(OutputInterface $output, array $entry) + /** + * @param array $entry + */ + private function prepareMagazines(OutputInterface $output, array $entry): void { try { $command = $this->getApplication()->find('mbin:user:create'); diff --git a/src/Command/AwesomeBot/AwesomeBotMagazine.php b/src/Command/AwesomeBot/AwesomeBotMagazine.php index 76ba1565f..bfe050cd0 100644 --- a/src/Command/AwesomeBot/AwesomeBotMagazine.php +++ b/src/Command/AwesomeBot/AwesomeBotMagazine.php @@ -79,6 +79,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } + /** + * @param string[] $tags + */ #[Pure] private function createBadges(Magazine $magazine, string $url, array $tags): Collection { diff --git a/src/Command/MoveEntriesByTagCommand.php b/src/Command/MoveEntriesByTagCommand.php index eaf133320..eca167d0d 100644 --- a/src/Command/MoveEntriesByTagCommand.php +++ b/src/Command/MoveEntriesByTagCommand.php @@ -4,7 +4,6 @@ namespace App\Command; -use App\Entity\Entry; use App\Entity\EntryComment; use App\Entity\Favourite; use App\Entity\Magazine; @@ -84,7 +83,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function moveComments(ArrayCollection|Collection $comments, Magazine $magazine) + /** + * @param ArrayCollection|Collection $comments + */ + private function moveComments(ArrayCollection|Collection $comments, Magazine $magazine): void { foreach ($comments as $comment) { /* @@ -99,7 +101,10 @@ private function moveComments(ArrayCollection|Collection $comments, Magazine $ma } } - private function moveReports(ArrayCollection|Collection $reports, Magazine $magazine) + /** + * @param ArrayCollection|Collection $reports + */ + private function moveReports(ArrayCollection|Collection $reports, Magazine $magazine): void { foreach ($reports as $report) { /* @@ -111,7 +116,10 @@ private function moveReports(ArrayCollection|Collection $reports, Magazine $maga } } - private function moveFavourites(ArrayCollection|Collection $favourites, Magazine $magazine) + /** + * @param ArrayCollection|Collection $favourites + */ + private function moveFavourites(ArrayCollection|Collection $favourites, Magazine $magazine): void { foreach ($favourites as $favourite) { /* diff --git a/src/Command/MovePostsByTagCommand.php b/src/Command/MovePostsByTagCommand.php index 33c3e91a7..56f9d773a 100644 --- a/src/Command/MovePostsByTagCommand.php +++ b/src/Command/MovePostsByTagCommand.php @@ -4,7 +4,10 @@ namespace App\Command; +use App\Entity\EntryComment; +use App\Entity\Favourite; use App\Entity\Magazine; +use App\Entity\Report; use App\Repository\MagazineRepository; use App\Repository\PostRepository; use App\Service\PostManager; @@ -69,7 +72,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function moveComments(ArrayCollection|Collection $comments, Magazine $magazine) + /** + * @param ArrayCollection|Collection $comments + */ + private function moveComments(ArrayCollection|Collection $comments, Magazine $magazine): void { foreach ($comments as $comment) { /* @@ -84,7 +90,10 @@ private function moveComments(ArrayCollection|Collection $comments, Magazine $ma } } - private function moveReports(ArrayCollection|Collection $reports, Magazine $magazine) + /** + * @param ArrayCollection|Collection $reports + */ + private function moveReports(ArrayCollection|Collection $reports, Magazine $magazine): void { foreach ($reports as $report) { /* @@ -96,7 +105,10 @@ private function moveReports(ArrayCollection|Collection $reports, Magazine $maga } } - private function moveFavourites(ArrayCollection|Collection $favourites, Magazine $magazine) + /** + * @param ArrayCollection|Collection $favourites + */ + private function moveFavourites(ArrayCollection|Collection $favourites, Magazine $magazine): void { foreach ($favourites as $favourite) { /* diff --git a/src/Command/RemoveDeadMessagesCommand.php b/src/Command/RemoveDeadMessagesCommand.php index 5acd13a8d..0b76e1eac 100644 --- a/src/Command/RemoveDeadMessagesCommand.php +++ b/src/Command/RemoveDeadMessagesCommand.php @@ -39,7 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Remove all dead messages from database. */ - private function removeDeadMessages() + private function removeDeadMessages(): void { $this->entityManager->getConnection()->executeQuery( 'DELETE FROM messenger_messages WHERE queue_name = ?', diff --git a/src/Command/RemoveDuplicatesCommand.php b/src/Command/RemoveDuplicatesCommand.php index 472938516..910184515 100644 --- a/src/Command/RemoveDuplicatesCommand.php +++ b/src/Command/RemoveDuplicatesCommand.php @@ -39,7 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function removePosts() + private function removePosts(): void { $conn = $this->entityManager->getConnection(); $sql = ' @@ -67,7 +67,7 @@ private function removePosts() } } - private function removeActors() + private function removeActors(): void { $conn = $this->entityManager->getConnection(); $sql = ' diff --git a/src/Command/RemoveFailedMessagesCommand.php b/src/Command/RemoveFailedMessagesCommand.php index 466e71c4d..2e6b1d3db 100644 --- a/src/Command/RemoveFailedMessagesCommand.php +++ b/src/Command/RemoveFailedMessagesCommand.php @@ -39,7 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Remove all failed messages from database. */ - private function removeFailedMessages() + private function removeFailedMessages(): void { $this->entityManager->getConnection()->executeQuery( 'DELETE FROM messenger_messages WHERE queue_name = ?', diff --git a/src/Command/Update/ApKeysUpdateCommand.php b/src/Command/Update/ApKeysUpdateCommand.php index 75b37054e..1b6ffcda4 100644 --- a/src/Command/Update/ApKeysUpdateCommand.php +++ b/src/Command/Update/ApKeysUpdateCommand.php @@ -55,11 +55,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function generate(array $actors) + /** + * @param ActivityPubActorInterface[] $actors + */ + private function generate(array $actors): void { - /** - * @var $actor ActivityPubActorInterface - */ foreach ($actors as $actor) { $actor = KeysGenerator::generate($actor); $this->entityManager->persist($actor); diff --git a/src/Command/Update/Async/ImageBlurhashHandler.php b/src/Command/Update/Async/ImageBlurhashHandler.php index 08903189a..31ad92c89 100644 --- a/src/Command/Update/Async/ImageBlurhashHandler.php +++ b/src/Command/Update/Async/ImageBlurhashHandler.php @@ -5,6 +5,7 @@ namespace App\Command\Update\Async; use App\Entity\Image; +use App\Repository\ImageRepository; use App\Service\ImageManager; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -18,8 +19,12 @@ public function __construct( ) { } - public function __invoke(ImageBlurhashMessage $message) + /** + * @return true + */ + public function __invoke(ImageBlurhashMessage $message): bool { + /** @var ImageRepository $repo */ $repo = $this->entityManager->getRepository(Image::class); $image = $repo->find($message->id); diff --git a/src/Command/Update/Async/NoteVisibilityHandler.php b/src/Command/Update/Async/NoteVisibilityHandler.php index f8f707fff..294842613 100644 --- a/src/Command/Update/Async/NoteVisibilityHandler.php +++ b/src/Command/Update/Async/NoteVisibilityHandler.php @@ -5,8 +5,6 @@ namespace App\Command\Update\Async; use App\Entity\Contracts\VisibilityInterface; -use App\Entity\Post; -use App\Entity\PostComment; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -21,13 +19,10 @@ public function __construct( ) { } - public function __invoke(NoteVisibilityMessage $message) + public function __invoke(NoteVisibilityMessage $message): void { $repo = $this->entityManager->getRepository($message->class); - /** - * @var $entity Post|PostComment - */ $entity = $repo->find($message->id); $req = $this->client->request('GET', $entity->apId, [ 'headers' => [ diff --git a/src/Command/Update/Async/NoteVisibilityMessage.php b/src/Command/Update/Async/NoteVisibilityMessage.php index ae3972cae..4e479f16d 100644 --- a/src/Command/Update/Async/NoteVisibilityMessage.php +++ b/src/Command/Update/Async/NoteVisibilityMessage.php @@ -4,10 +4,15 @@ namespace App\Command\Update\Async; +use App\Entity\Post; +use App\Entity\PostComment; use App\Message\Contracts\AsyncMessageInterface; class NoteVisibilityMessage implements AsyncMessageInterface { + /** + * @param class-string $class + */ public function __construct(public int $id, public string $class) { } diff --git a/src/Command/Update/PostCommentRootUpdateCommand.php b/src/Command/Update/PostCommentRootUpdateCommand.php index 371d77383..50e61fcff 100644 --- a/src/Command/Update/PostCommentRootUpdateCommand.php +++ b/src/Command/Update/PostCommentRootUpdateCommand.php @@ -44,7 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - private function update(PostComment $comment) + private function update(PostComment $comment): void { if (null === $comment->parent->root) { $this->entityManager->getConnection()->executeQuery( diff --git a/src/Command/Update/TagsUpdateCommand.php b/src/Command/Update/TagsUpdateCommand.php index 5c826b921..d4a27116a 100644 --- a/src/Command/Update/TagsUpdateCommand.php +++ b/src/Command/Update/TagsUpdateCommand.php @@ -31,18 +31,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $comments = $this->entityManager->getRepository(EntryComment::class)->findAll(); foreach ($comments as $comment) { + // TODO: $comment->tags is undefined; should it be ->hashtags? $comment->tags = $this->tagExtractor->extract($comment->body, $comment->magazine->name); $this->entityManager->persist($comment); } $posts = $this->entityManager->getRepository(Post::class)->findAll(); foreach ($posts as $post) { + // TODO: $post->tags is undefined; should it be ->hashtags? $post->tags = $this->tagExtractor->extract($post->body, $post->magazine->name); $this->entityManager->persist($post); } $comments = $this->entityManager->getRepository(PostComment::class)->findAll(); foreach ($comments as $comment) { + // TODO: $comment->tags is undefined; should it be ->hashtags? $comment->tags = $this->tagExtractor->extract($comment->body, $comment->magazine->name); $this->entityManager->persist($comment); } diff --git a/src/Command/Update/UserLastActiveUpdateCommand.php b/src/Command/Update/UserLastActiveUpdateCommand.php index 18b4e9b22..3b462eb06 100644 --- a/src/Command/Update/UserLastActiveUpdateCommand.php +++ b/src/Command/Update/UserLastActiveUpdateCommand.php @@ -5,6 +5,7 @@ namespace App\Command\Update; use App\Entity\User; +use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -24,6 +25,7 @@ public function __construct(private readonly EntityManagerInterface $entityManag protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var UserRepository $repo */ $repo = $this->entityManager->getRepository(User::class); $hideAdult = false; diff --git a/src/Controller/ActivityPub/NodeInfoController.php b/src/Controller/ActivityPub/NodeInfoController.php index 3690cdc1e..08870039a 100644 --- a/src/Controller/ActivityPub/NodeInfoController.php +++ b/src/Controller/ActivityPub/NodeInfoController.php @@ -39,6 +39,8 @@ public function nodeInfoV2(string $version): JsonResponse /** * Get list of links for well-known nodeinfo. + * + * @return array[]> */ private function getLinks(): array { diff --git a/src/Controller/Admin/AdminDashboardController.php b/src/Controller/Admin/AdminDashboardController.php index 154289a7c..be3a7465c 100644 --- a/src/Controller/Admin/AdminDashboardController.php +++ b/src/Controller/Admin/AdminDashboardController.php @@ -6,6 +6,7 @@ use App\Controller\AbstractController; use App\Service\InstanceStatsManager; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Attribute\IsGranted; class AdminDashboardController extends AbstractController @@ -15,7 +16,7 @@ public function __construct(private readonly InstanceStatsManager $counter) } #[IsGranted('ROLE_ADMIN')] - public function __invoke(?int $statsPeriod, ?bool $withFederated) + public function __invoke(?int $statsPeriod, ?bool $withFederated): Response { if (!$statsPeriod or -1 === $statsPeriod) { $statsPeriod = null; diff --git a/src/Controller/Admin/AdminFederationController.php b/src/Controller/Admin/AdminFederationController.php index c41e32c0b..c583da63f 100644 --- a/src/Controller/Admin/AdminFederationController.php +++ b/src/Controller/Admin/AdminFederationController.php @@ -9,6 +9,7 @@ use App\Form\InstancesType; use App\Service\SettingsManager; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Attribute\IsGranted; class AdminFederationController extends AbstractController @@ -18,7 +19,7 @@ public function __construct(public readonly SettingsManager $settingsManager) } #[IsGranted('ROLE_ADMIN')] - public function __invoke(Request $request) + public function __invoke(Request $request): Response { $dto = new InstancesDto($this->settingsManager->get('KBIN_BANNED_INSTANCES')); diff --git a/src/Controller/Admin/AdminUsersController.php b/src/Controller/Admin/AdminUsersController.php index 6d57f9596..41add57a5 100644 --- a/src/Controller/Admin/AdminUsersController.php +++ b/src/Controller/Admin/AdminUsersController.php @@ -7,6 +7,7 @@ use App\Controller\AbstractController; use App\Repository\UserRepository; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Attribute\IsGranted; class AdminUsersController extends AbstractController @@ -16,7 +17,7 @@ public function __construct(private readonly UserRepository $repository, private } #[IsGranted('ROLE_ADMIN')] - public function active(?bool $withFederated = null) + public function active(?bool $withFederated = null): Response { return $this->render( 'admin/users.html.twig', @@ -31,7 +32,7 @@ public function active(?bool $withFederated = null) } #[IsGranted('ROLE_ADMIN')] - public function inactive() + public function inactive(): Response { return $this->render( 'admin/users.html.twig', @@ -44,7 +45,7 @@ public function inactive() } #[IsGranted('ROLE_ADMIN')] - public function suspended(?bool $withFederated = null) + public function suspended(?bool $withFederated = null): Response { return $this->render( 'admin/users.html.twig', @@ -59,7 +60,7 @@ public function suspended(?bool $withFederated = null) } #[IsGranted('ROLE_ADMIN')] - public function banned(?bool $withFederated = null) + public function banned(?bool $withFederated = null): Response { return $this->render( 'admin/users.html.twig', diff --git a/src/Controller/Api/BaseApi.php b/src/Controller/Api/BaseApi.php index 2f81f8578..f2849ca1f 100644 --- a/src/Controller/Api/BaseApi.php +++ b/src/Controller/Api/BaseApi.php @@ -6,6 +6,7 @@ use App\Controller\AbstractController; use App\DTO\MagazineDto; +use App\DTO\MagazineResponseDto; use App\DTO\ReportDto; use App\DTO\ReportRequestDto; use App\DTO\UserDto; @@ -55,6 +56,7 @@ use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Validator\Constraints\Image as BaseImageConstraint; use Symfony\Component\Validator\Validator\ValidatorInterface; class BaseApi extends AbstractController @@ -65,6 +67,7 @@ class BaseApi extends AbstractController public const MIN_DEPTH = 0; public const MAX_DEPTH = 25; + /** @var BaseImageConstraint */ private static $constraint; public function __construct( @@ -97,7 +100,7 @@ public function __construct( * @param ?RateLimiterFactory $limiterFactory A limiter factory to use when the user is authenticated * @param ?RateLimiterFactory $anonLimiterFactory A limiter factory to use when the user is anonymous * - * @return array An array of headers describing the current rate limit status to the client + * @return array An array of headers describing the current rate limit status to the client * * @throws AccessDeniedHttpException if the user is not authenticated and no anonymous rate limiter factory is provided, access to the resource will be denied * @throws TooManyRequestsHttpException If the limit is hit, rate limit the connection @@ -143,7 +146,7 @@ protected function rateLimit( * This might be better to have as a cache entry, with an aggregate in the database * created periodically */ - private function logAccess() + private function logAccess(): void { /** @var ?OAuth2Token $token */ $token = $this->container->get('security.token_storage')->getToken(); @@ -254,7 +257,7 @@ public function serializeContentInterface(ContentInterface $content, bool $force */ protected function serializeLogItem(MagazineLog $log): array { - /** @var ContentVisibilityInterface $subject */ + /** @var ?ContentVisibilityInterface $subject */ $subject = $log->getSubject(); $response = $this->magazineFactory->createLogDto($log); $response->setSubject( @@ -287,7 +290,7 @@ protected function serializeLogItem(MagazineLog $log): array * * @param MagazineDto $dto The MagazineDto to serialize * - * @return array An associative array representation of the entry's safe fields, to be used as JSON + * @return MagazineResponseDto An associative array representation of the entry's safe fields, to be used as JSON */ protected function serializeMagazine(MagazineDto $dto) { diff --git a/src/Controller/Api/Domain/DomainBaseApi.php b/src/Controller/Api/Domain/DomainBaseApi.php index 82fb74c6b..1558bb451 100644 --- a/src/Controller/Api/Domain/DomainBaseApi.php +++ b/src/Controller/Api/Domain/DomainBaseApi.php @@ -15,7 +15,7 @@ class DomainBaseApi extends BaseApi private readonly DomainFactory $factory; #[Required] - public function setFactory(DomainFactory $factory) + public function setFactory(DomainFactory $factory): void { $this->factory = $factory; } @@ -23,7 +23,7 @@ public function setFactory(DomainFactory $factory) /** * Serialize a domain to JSON. */ - protected function serializeDomain(DomainDto|Domain $dto) + protected function serializeDomain(DomainDto|Domain $dto): DomainDto { $response = $dto instanceof Domain ? $this->factory->createDto($dto) : $dto; diff --git a/src/Controller/Api/Entry/EntriesBaseApi.php b/src/Controller/Api/Entry/EntriesBaseApi.php index 8b34c280f..d955cc260 100644 --- a/src/Controller/Api/Entry/EntriesBaseApi.php +++ b/src/Controller/Api/Entry/EntriesBaseApi.php @@ -27,7 +27,7 @@ class EntriesBaseApi extends BaseApi private EntryCommentFactory $commentsFactory; #[Required] - public function setCommentsFactory(EntryCommentFactory $commentsFactory) + public function setCommentsFactory(EntryCommentFactory $commentsFactory): void { $this->commentsFactory = $commentsFactory; } diff --git a/src/DTO/MagazineLogResponseDto.php b/src/DTO/MagazineLogResponseDto.php index 368a53ec0..1d9a39382 100644 --- a/src/DTO/MagazineLogResponseDto.php +++ b/src/DTO/MagazineLogResponseDto.php @@ -4,6 +4,7 @@ namespace App\DTO; +use App\Entity\Contracts\ContentInterface; use App\Entity\Entry; use App\Entity\EntryComment; use App\Entity\Post; @@ -71,7 +72,7 @@ public static function create( #[Ignore] public function setSubject( - Entry|EntryComment|Post|PostComment|null $subject, + ?ContentInterface $subject, EntryFactory $entryFactory, EntryCommentFactory $entryCommentFactory, PostFactory $postFactory, diff --git a/src/Message/ActivityPub/Inbox/ActivityMessage.php b/src/Message/ActivityPub/Inbox/ActivityMessage.php index dcc6b9960..c0ea739d0 100644 --- a/src/Message/ActivityPub/Inbox/ActivityMessage.php +++ b/src/Message/ActivityPub/Inbox/ActivityMessage.php @@ -7,7 +7,7 @@ use App\Message\Contracts\ActivityPubInboxReceiveInterface; /** - * @phpstan-type RequestData array{host: string, method: string, uri: string, client: string} + * @phpstan-type RequestData array{host: string, method: string, uri: string, client_ip: string} */ class ActivityMessage implements ActivityPubInboxReceiveInterface { diff --git a/src/Repository/DomainRepository.php b/src/Repository/DomainRepository.php index 0ae467853..c15973a7a 100644 --- a/src/Repository/DomainRepository.php +++ b/src/Repository/DomainRepository.php @@ -12,7 +12,6 @@ use Pagerfanta\Doctrine\ORM\QueryAdapter; use Pagerfanta\Exception\NotValidCurrentPageException; use Pagerfanta\Pagerfanta; -use Pagerfanta\PagerfantaInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -31,7 +30,7 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Domain::class); } - public function findAllPaginated(?int $page): PagerfantaInterface + public function findAllPaginated(int $page, int $perPage = self::PER_PAGE): Pagerfanta { $qb = $this->createQueryBuilder('d'); @@ -42,7 +41,7 @@ public function findAllPaginated(?int $page): PagerfantaInterface ); try { - $pagerfanta->setMaxPerPage($criteria->perPage ?? self::PER_PAGE); + $pagerfanta->setMaxPerPage($perPage); $pagerfanta->setCurrentPage($page); } catch (NotValidCurrentPageException $e) { throw new NotFoundHttpException(); @@ -51,7 +50,7 @@ public function findAllPaginated(?int $page): PagerfantaInterface return $pagerfanta; } - public function findSubscribedDomains(int $page, User $user): PagerfantaInterface + public function findSubscribedDomains(int $page, User $user, int $perPage = self::PER_PAGE): Pagerfanta { $pagerfanta = new Pagerfanta( new CollectionAdapter( @@ -60,7 +59,7 @@ public function findSubscribedDomains(int $page, User $user): PagerfantaInterfac ); try { - $pagerfanta->setMaxPerPage(self::PER_PAGE); + $pagerfanta->setMaxPerPage($perPage); $pagerfanta->setCurrentPage($page); } catch (NotValidCurrentPageException $e) { throw new NotFoundHttpException(); @@ -69,7 +68,7 @@ public function findSubscribedDomains(int $page, User $user): PagerfantaInterfac return $pagerfanta; } - public function findBlockedDomains(int $page, User $user): PagerfantaInterface + public function findBlockedDomains(int $page, User $user, int $perPage = self::PER_PAGE): Pagerfanta { $pagerfanta = new Pagerfanta( new CollectionAdapter( @@ -78,7 +77,7 @@ public function findBlockedDomains(int $page, User $user): PagerfantaInterface ); try { - $pagerfanta->setMaxPerPage(self::PER_PAGE); + $pagerfanta->setMaxPerPage($perPage); $pagerfanta->setCurrentPage($page); } catch (NotValidCurrentPageException $e) { throw new NotFoundHttpException(); diff --git a/src/Repository/EntryCommentRepository.php b/src/Repository/EntryCommentRepository.php index ddb178c0f..dac63fc61 100644 --- a/src/Repository/EntryCommentRepository.php +++ b/src/Repository/EntryCommentRepository.php @@ -31,7 +31,6 @@ use Pagerfanta\Doctrine\ORM\QueryAdapter; use Pagerfanta\Exception\NotValidCurrentPageException; use Pagerfanta\Pagerfanta; -use Pagerfanta\PagerfantaInterface; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -54,7 +53,7 @@ public function __construct( parent::__construct($registry, EntryComment::class); } - public function findByCriteria(Criteria $criteria): PagerfantaInterface + public function findByCriteria(Criteria $criteria): Pagerfanta { $pagerfanta = new Pagerfanta( new QueryAdapter( diff --git a/src/Repository/EntryRepository.php b/src/Repository/EntryRepository.php index 2dfcbe7e8..d38890f21 100644 --- a/src/Repository/EntryRepository.php +++ b/src/Repository/EntryRepository.php @@ -32,15 +32,17 @@ use Doctrine\Persistence\ManagerRegistry; use Pagerfanta\Exception\NotValidCurrentPageException; use Pagerfanta\Pagerfanta; -use Pagerfanta\PagerfantaInterface; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; /** + * @extends ServiceEntityRepository + * * @method Entry|null find($id, $lockMode = null, $lockVersion = null) * @method Entry|null findOneBy(array $criteria, array $orderBy = null) + * @method Entry|null findOneByUrl(string $url) * @method Entry[] findAll() * @method Entry[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ @@ -60,7 +62,7 @@ public function __construct( parent::__construct($registry, Entry::class); } - public function findByCriteria(EntryPageView|Criteria $criteria): PagerfantaInterface + public function findByCriteria(EntryPageView|Criteria $criteria): Pagerfanta { $pagerfanta = new Pagerfanta($this->adapterFactory->create($this->getEntryQueryBuilder($criteria))); diff --git a/src/Service/SearchManager.php b/src/Service/SearchManager.php index 528a79cfe..73845d376 100644 --- a/src/Service/SearchManager.php +++ b/src/Service/SearchManager.php @@ -42,7 +42,7 @@ public function findMagazinesPaginated(string $magazine, int $page = 1, int $per return $this->magazineRepository->search($magazine, $page, $perPage); } - public function findDomainsPaginated(string $domain, int $page = 1, int $perPage = DomainRepository::PER_PAGE): PagerfantaInterface + public function findDomainsPaginated(string $domain, int $page = 1, int $perPage = DomainRepository::PER_PAGE): Pagerfanta { return $this->domainRepository->search($domain, $page, $perPage); } diff --git a/tests/Unit/ActivityPub/ActorHandleTest.php b/tests/Unit/ActivityPub/ActorHandleTest.php index d3f5a09b4..699e8f1fa 100644 --- a/tests/Unit/ActivityPub/ActorHandleTest.php +++ b/tests/Unit/ActivityPub/ActorHandleTest.php @@ -5,21 +5,18 @@ namespace App\Tests\Unit\ActivityPub; use App\ActivityPub\ActorHandle; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class ActorHandleTest extends TestCase { - /** - * @dataProvider handleProvider() - */ + #[DataProvider('handleProvider')] public function testHandleIsRecognized(string $input, array $output): void { $this->assertNotNull(ActorHandle::parse($input)); } - /** - * @dataProvider handleProvider() - */ + #[DataProvider('handleProvider')] public function testHandleIsParsedProperly(string $input, array $output): void { $handle = ActorHandle::parse($input); diff --git a/tests/Unit/Service/ActivityPub/SignatureValidatorTest.php b/tests/Unit/Service/ActivityPub/SignatureValidatorTest.php index 0a2637ac5..392672910 100644 --- a/tests/Unit/Service/ActivityPub/SignatureValidatorTest.php +++ b/tests/Unit/Service/ActivityPub/SignatureValidatorTest.php @@ -9,6 +9,7 @@ use App\Service\ActivityPub\ApHttpClient; use App\Service\ActivityPub\SignatureValidator; use App\Service\ActivityPubManager; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -82,9 +83,7 @@ private function createSignedRequest(string $inbox): void $this->headers = $headers; } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testItValidatesACorrectlySignedRequest(): void { $this->createSignedRequest('/f/inbox'); @@ -115,9 +114,7 @@ public function testItValidatesACorrectlySignedRequest(): void $sut->validate(['uri' => '/f/inbox'], $this->headers, json_encode($this->body)); } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testItValidatesACorrectlySignedRequestToAPersonalInbox(): void { $this->createSignedRequest('/u/user/inbox'); diff --git a/tests/Unit/Service/TagExtractorTest.php b/tests/Unit/Service/TagExtractorTest.php index 6716b7fa0..35e84a7e4 100644 --- a/tests/Unit/Service/TagExtractorTest.php +++ b/tests/Unit/Service/TagExtractorTest.php @@ -5,13 +5,12 @@ namespace App\Tests\Unit\Service; use App\Service\TagExtractor; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class TagExtractorTest extends TestCase { - /** - * @dataProvider provider - */ + #[DataProvider('provider')] public function testExtract(string $input, ?array $output): void { $this->assertEquals($output, (new TagExtractor())->extract($input, 'kbin')); diff --git a/tests/Unit/Utils/SluggerTest.php b/tests/Unit/Utils/SluggerTest.php index 63233558a..20fc3d6d0 100644 --- a/tests/Unit/Utils/SluggerTest.php +++ b/tests/Unit/Utils/SluggerTest.php @@ -5,13 +5,12 @@ namespace App\Tests\Unit\Utils; use App\Utils\Slugger; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class SluggerTest extends TestCase { - /** - * @dataProvider provider - */ + #[DataProvider('provider')] public function testCamelCase(string $input, string $output): void { $this->assertEquals($output, Slugger::camelCase($input));