From 2fd46ca886d183f544ec95da33e070f76edf6cd3 Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Wed, 19 Oct 2022 22:39:17 +0100 Subject: [PATCH] [TOOLS] Continue raising PHPStan level to 6 --- .php-cs-fixer.php | 2 +- components/Circle/Circle.php | 4 +- components/Circle/Controller/Circles.php | 6 +- components/Collection/Collection.php | 94 ++++++++++--------- .../Util/Controller/CircleController.php | 3 + .../Collection/Util/Controller/Collection.php | 6 +- .../Util/Controller/FeedController.php | 13 ++- .../Controller/MetaCollectionController.php | 40 ++++---- .../Util/Controller/OrderedCollection.php | 7 +- .../Collection/Util/MetaCollectionTrait.php | 16 ++-- components/Collection/Util/Parser.php | 3 + .../Conversation/Controller/Conversation.php | 12 ++- components/Conversation/Conversation.php | 29 ++++-- components/Feed/Controller/Feeds.php | 7 ++ components/FreeNetwork/FreeNetwork.php | 10 ++ components/Group/Controller/Group.php | 4 + components/Group/Controller/GroupFeed.php | 9 ++ components/Group/Group.php | 8 ++ components/Language/Controller/Language.php | 4 +- components/Language/Entity/ActorLanguage.php | 3 + components/Language/Entity/Language.php | 7 ++ components/Language/Language.php | 6 ++ components/LeftPanel/LeftPanel.php | 2 + components/Link/Entity/NoteToLink.php | 2 + components/Link/Link.php | 10 +- components/Notification/Controller/Feed.php | 2 + .../Notification/Entity/Notification.php | 8 +- components/Notification/Notification.php | 21 +++-- components/Person/Controller/PersonFeed.php | 10 ++ .../Person/Controller/PersonSettings.php | 6 +- components/Posting/Posting.php | 51 +++++++--- components/Search/Controller/Search.php | 7 +- components/Search/Search.php | 4 +- .../Subscription/Controller/Subscribers.php | 8 ++ .../Subscription/Controller/Subscriptions.php | 7 +- components/Subscription/Subscription.php | 9 +- components/Tag/Controller/Tag.php | 17 +++- components/Tag/Entity/NoteTag.php | 3 + components/Tag/Entity/NoteTagBlock.php | 2 + components/Tag/Tag.php | 17 +++- phpstan.neon | 46 +++++++-- .../AttachmentCollections.php | 24 ++++- .../Controller/AttachmentCollections.php | 28 ++++-- .../AttachmentShowRelated.php | 4 +- plugins/AudioEncoder/AudioEncoder.php | 8 ++ plugins/Blog/Blog.php | 4 + plugins/Bundles/Bundles.php | 17 +++- ...leCollections.php => BundleCollection.php} | 18 ++-- plugins/Cover/Controller/Cover.php | 6 +- plugins/Cover/Cover.php | 2 +- plugins/Cover/Entity/Cover.php | 2 +- plugins/DeleteNote/DeleteNote.php | 2 + .../EmailNotifications/EmailNotifications.php | 2 +- plugins/Embed/Controller/OEmbed.php | 4 +- plugins/Favourite/Controller/Favourite.php | 5 +- plugins/LatexNotes/LatexNotes.php | 3 +- plugins/MarkdownNotes/MarkdownNotes.php | 3 +- .../NoteTypeFeedFilter/NoteTypeFeedFilter.php | 2 +- plugins/OAuth2/Entity/AccessToken.php | 3 + plugins/OAuth2/Entity/AuthCode.php | 3 + plugins/OAuth2/Repository/RefreshToken.php | 6 ++ .../PinnedNotes/Controller/PinnedNotes.php | 4 + plugins/PinnedNotes/Entity/PinnedNotes.php | 9 +- plugins/PinnedNotes/PinnedNotes.php | 4 +- plugins/UnboundGroup/UnboundGroup.php | 2 +- plugins/WebMonetization/WebMonetization.php | 9 +- .../XMPPNotifications/XMPPNotifications.php | 2 +- src/Core/Cache.php | 14 ++- src/Core/Controller.php | 11 ++- src/Core/DB.php | 16 ++-- src/Core/Form.php | 4 +- src/Core/GSFile.php | 2 +- src/Core/I18n/I18n.php | 44 ++++----- src/Core/I18n/TransExtractor.php | 4 + src/Core/Log.php | 3 +- src/Core/ModuleManager.php | 6 +- src/Core/Modules/Module.php | 2 +- src/Core/Queue.php | 4 +- src/Core/Router.php | 18 ++-- src/Core/Router/RouteLoader.php | 12 ++- src/Core/Security.php | 3 +- .../Compiler/SchemaDefDriver.php | 41 ++++---- src/Entity/Actor.php | 4 + .../ResetPasswordRequestRepository.php | 2 + src/Twig/Runtime.php | 14 ++- src/Util/Common.php | 9 +- src/Util/Formatting.php | 4 +- src/Util/HTML.php | 2 +- src/Util/TemporaryFile.php | 4 +- 89 files changed, 646 insertions(+), 278 deletions(-) rename plugins/Bundles/Controller/{BundleCollections.php => BundleCollection.php} (81%) diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index dba4bbcaf0..9261cad644 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -178,7 +178,7 @@ return $config // There MUST NOT be a space after the opening parenthesis. There MUST NOT be a space before the closing parenthesis. 'no_spaces_inside_parenthesis' => true, // Removes `@param`, `@return` and `@var` tags that don't provide any useful information. - 'no_superfluous_phpdoc_tags' => true, + 'no_superfluous_phpdoc_tags' => false, // Remove trailing commas in list function calls. 'no_trailing_comma_in_list_call' => true, // PHP single-line arrays should not have trailing comma. diff --git a/components/Circle/Circle.php b/components/Circle/Circle.php index 32aa8087e1..93947002cc 100644 --- a/components/Circle/Circle.php +++ b/components/Circle/Circle.php @@ -54,7 +54,7 @@ use Symfony\Component\HttpFoundation\Request; */ class Circle extends Component { - /** @phpstan-use MetaCollectionTrait */ + /** @phpstan-use MetaCollectionTrait */ use MetaCollectionTrait; public const TAG_CIRCLE_REGEX = '/' . Nickname::BEFORE_MENTIONS . '@#([\pL\pN_\-\.]{1,64})/'; protected const SLUG = 'circle'; @@ -228,7 +228,7 @@ class Circle extends Component * @param null|array $vars Page vars sent by AppendRightPanelBlock event * @param bool $ids_only true if only the Collections ids are to be returned * - * @return Circle[]|int[] + * @return ($ids_only is true ? int[] : ActorCircle[]) */ protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array { diff --git a/components/Circle/Controller/Circles.php b/components/Circle/Controller/Circles.php index 6748046f04..9f3f31552d 100644 --- a/components/Circle/Controller/Circles.php +++ b/components/Circle/Controller/Circles.php @@ -90,14 +90,12 @@ class Circles extends MetaCollectionController return $this->getCollectionItems($tagger_id, $circle_id); } - /** - * @return ActorCircle[] - */ public function getCollectionsByActorId(int $owner_id): array { return DB::findBy(ActorCircle::class, ['tagger' => $owner_id], order_by: ['id' => 'desc']); } - public function getCollectionBy(int $owner_id, int $collection_id): ActorCircle + + public function getCollectionBy(int $owner_id, int $collection_id): self { return DB::findOneBy(ActorCircle::class, ['id' => $collection_id, 'actor_id' => $owner_id]); } diff --git a/components/Collection/Collection.php b/components/Collection/Collection.php index 6d67179411..80c53b72db 100644 --- a/components/Collection/Collection.php +++ b/components/Collection/Collection.php @@ -26,7 +26,8 @@ class Collection extends Component * * @param array $note_order_by * @param array $actor_order_by - * @return array{notes: Note[], actors: Actor[]} + * + * @return array{notes: null|Note[], actors: null|Actor[]} */ public static function query(string $query, int $page, ?string $locale = null, ?Actor $actor = null, array $note_order_by = [], array $actor_order_by = []): array { @@ -84,6 +85,9 @@ class Collection extends Component /** * Convert $term to $note_expr and $actor_expr, search criteria. Handles searching for text * notes, for different types of actors and for the content of text notes + * + * @param mixed $note_expr + * @param mixed $actor_expr */ public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult { @@ -91,50 +95,50 @@ class Collection extends Component $term = explode(':', $term); if (Formatting::startsWith($term[0], 'note')) { switch ($term[0]) { - case 'notes-all': - $note_expr = $eb->neq('note.created', null); - break; - case 'note-local': - $note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN)); - break; - case 'note-types': - case 'notes-include': - case 'note-filter': - if (\is_null($note_expr)) { - $note_expr = []; - } - if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) { - $note_expr[] = $eb->neq('note.content', null); - } else { - $note_expr[] = $eb->eq('note.content', null); - } - break; - case 'note-conversation': - $note_expr = $eb->eq('note.conversation_id', (int) trim($term[1])); - break; - case 'note-from': - case 'notes-from': - $subscribed_expr = $eb->eq('subscription.subscriber_id', $actor->getId()); - $type_consts = []; - if ($term[1] === 'subscribed') { - $type_consts = null; - } - foreach (explode(',', $term[1]) as $from) { - if (str_starts_with($from, 'subscribed-')) { - [, $type] = explode('-', $from); - if (\in_array($type, ['actor', 'actors'])) { - $type_consts = null; - } else { - $type_consts[] = \constant(Actor::class . '::' . mb_strtoupper($type === 'organisation' ? 'group' : $type)); + case 'notes-all': + $note_expr = $eb->neq('note.created', null); + break; + case 'note-local': + $note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN)); + break; + case 'note-types': + case 'notes-include': + case 'note-filter': + if (\is_null($note_expr)) { + $note_expr = []; + } + if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) { + $note_expr[] = $eb->neq('note.content', null); + } else { + $note_expr[] = $eb->eq('note.content', null); + } + break; + case 'note-conversation': + $note_expr = $eb->eq('note.conversation_id', (int) trim($term[1])); + break; + case 'note-from': + case 'notes-from': + $subscribed_expr = $eb->eq('subscription.subscriber_id', $actor->getId()); + $type_consts = []; + if ($term[1] === 'subscribed') { + $type_consts = null; + } + foreach (explode(',', $term[1]) as $from) { + if (str_starts_with($from, 'subscribed-')) { + [, $type] = explode('-', $from); + if (\in_array($type, ['actor', 'actors'])) { + $type_consts = null; + } else { + $type_consts[] = \constant(Actor::class . '::' . mb_strtoupper($type === 'organisation' ? 'group' : $type)); + } } } - } - if (\is_null($type_consts)) { - $note_expr = $subscribed_expr; - } elseif (!empty($type_consts)) { - $note_expr = $eb->andX($subscribed_expr, $eb->in('note_actor.type', $type_consts)); - } - break; + if (\is_null($type_consts)) { + $note_expr = $subscribed_expr; + } elseif (!empty($type_consts)) { + $note_expr = $eb->andX($subscribed_expr, $eb->in('note_actor.type', $type_consts)); + } + break; } } elseif (Formatting::startsWith($term, 'actor-')) { switch ($term[0]) { @@ -148,8 +152,8 @@ class Collection extends Component foreach ( [ Actor::PERSON => ['person', 'people'], - Actor::GROUP => ['group', 'groups', 'org', 'orgs', 'organisation', 'organisations', 'organization', 'organizations'], - Actor::BOT => ['bot', 'bots'], + Actor::GROUP => ['group', 'groups', 'org', 'orgs', 'organisation', 'organisations', 'organization', 'organizations'], + Actor::BOT => ['bot', 'bots'], ] as $type => $match) { if (array_intersect(explode(',', $term[1]), $match) !== []) { $actor_expr[] = $eb->eq('actor.type', $type); diff --git a/components/Collection/Util/Controller/CircleController.php b/components/Collection/Util/Controller/CircleController.php index 47c11b9290..4a54dd91f0 100644 --- a/components/Collection/Util/Controller/CircleController.php +++ b/components/Collection/Util/Controller/CircleController.php @@ -4,6 +4,9 @@ declare(strict_types = 1); namespace Component\Collection\Util\Controller; +/** + * @extends OrderedCollection<\Component\Circle\Entity\ActorCircle> + */ class CircleController extends OrderedCollection { } diff --git a/components/Collection/Util/Controller/Collection.php b/components/Collection/Util/Controller/Collection.php index de8490e491..0273b59f94 100644 --- a/components/Collection/Util/Controller/Collection.php +++ b/components/Collection/Util/Controller/Collection.php @@ -6,18 +6,20 @@ namespace Component\Collection\Util\Controller; use App\Core\Controller; use App\Entity\Actor; +use App\Entity\Note; use App\Util\Common; use Component\Collection\Collection as CollectionComponent; /** * @template T */ -class Collection extends Controller +abstract class Collection extends Controller { /** * @param array $note_order_by * @param array $actor_order_by - * @return array + * + * @return array{notes: null|Note[], actors: null|Actor[]} */ public function query(string $query, ?string $locale = null, ?Actor $actor = null, array $note_order_by = [], array $actor_order_by = []): array { diff --git a/components/Collection/Util/Controller/FeedController.php b/components/Collection/Util/Controller/FeedController.php index 35cf8f80e4..a7f609d4f5 100644 --- a/components/Collection/Util/Controller/FeedController.php +++ b/components/Collection/Util/Controller/FeedController.php @@ -38,6 +38,11 @@ use App\Entity\Note; use App\Util\Common; use Functional as F; +/** + * @template T + * + * @extends OrderedCollection + */ abstract class FeedController extends OrderedCollection { /** @@ -45,9 +50,11 @@ abstract class FeedController extends OrderedCollection * notes or actors the user specified, as well as format the raw * list of notes into a usable format * - * @template T of Note|Actor - * @param T[] $result - * @return T[] + * @template NA of Note|Actor + * + * @param NA[] $result + * + * @return NA[] */ protected function postProcess(array $result): array { diff --git a/components/Collection/Util/Controller/MetaCollectionController.php b/components/Collection/Util/Controller/MetaCollectionController.php index 7bcc12a7ce..1d85763580 100644 --- a/components/Collection/Util/Controller/MetaCollectionController.php +++ b/components/Collection/Util/Controller/MetaCollectionController.php @@ -39,10 +39,13 @@ use App\Util\Common; use App\Util\Exception\RedirectException; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormView; use Symfony\Component\HttpFoundation\Request; /** - * @template T + * @template T of object + * + * @extends FeedController */ abstract class MetaCollectionController extends FeedController { @@ -70,7 +73,7 @@ abstract class MetaCollectionController extends FeedController abstract public function createCollection(int $owner_id, string $name): bool; /** - * @return T[] + * @return ControllerResultType */ public function collectionsViewByActorNickname(Request $request, string $nickname): array { @@ -79,7 +82,7 @@ abstract class MetaCollectionController extends FeedController } /** - * @return T[] + * @return ControllerResultType */ public function collectionsViewByActorId(Request $request, int $id): array { @@ -135,34 +138,23 @@ abstract class MetaCollectionController extends FeedController // the functions and passing that class to the template. // This is suggested at https://web.archive.org/web/20220226132328/https://stackoverflow.com/questions/3595727/twig-pass-function-into-template/50364502 $fn = new class($id, $nickname, $request, $this, static::SLUG) { - private $id; - private $nick; - private $request; - private $parent; - private $slug; - - public function __construct($id, $nickname, $request, $parent, $slug) + public function __construct(private int $id, private string $nickname, private Request $request, private object $parent, private string $slug) { - $this->id = $id; - $this->nick = $nickname; - $this->request = $request; - $this->parent = $parent; - $this->slug = $slug; } // there's already an injected function called path, // that maps to Router::url(name, args), but since // I want to preserve nicknames, I think it's better // to use that getUrl function - public function getUrl($cid) + public function getUrl(int $cid): string { - return $this->parent->getCollectionUrl($this->id, $this->nick, $cid); + return $this->parent->getCollectionUrl($this->id, $this->nickname, $cid); } // There are many collections in this page and we need two // forms for each one of them: one form to edit the collection's // name and another to remove the collection. // creating the edit form - public function editForm($collection) + public function editForm(object $collection): FormView { $edit = Form::create([ ['name', TextType::class, [ @@ -181,7 +173,7 @@ abstract class MetaCollectionController extends FeedController ]); $edit->handleRequest($this->request); if ($edit->isSubmitted() && $edit->isValid()) { - $this->parent->setCollectionName($this->id, $this->nick, $collection, $edit->getData()['name']); + $this->parent->setCollectionName($this->id, $this->nickname, $collection, $edit->getData()['name']); DB::flush(); throw new RedirectException(); } @@ -189,7 +181,7 @@ abstract class MetaCollectionController extends FeedController } // creating the remove form - public function rmForm($collection) + public function rmForm(object $collection): FormView { $rm = Form::create([ ['remove_' . $collection->getId(), SubmitType::class, [ @@ -202,7 +194,7 @@ abstract class MetaCollectionController extends FeedController ]); $rm->handleRequest($this->request); if ($rm->isSubmitted()) { - $this->parent->removeCollection($this->id, $this->nick, $collection); + $this->parent->removeCollection($this->id, $this->nickname, $collection); DB::flush(); throw new RedirectException(); } @@ -220,12 +212,18 @@ abstract class MetaCollectionController extends FeedController ]; } + /** + * @return ControllerResultType + */ public function collectionsEntryViewNotesByNickname(Request $request, string $nickname, int $cid): array { $user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]); return self::collectionsEntryViewNotesByActorId($request, $user->getId(), $cid); } + /** + * @return ControllerResultType + */ public function collectionsEntryViewNotesByActorId(Request $request, int $id, int $cid): array { $collection = $this->getCollectionBy($id, $cid); diff --git a/components/Collection/Util/Controller/OrderedCollection.php b/components/Collection/Util/Controller/OrderedCollection.php index 7a61f1ab46..1a48c70e72 100644 --- a/components/Collection/Util/Controller/OrderedCollection.php +++ b/components/Collection/Util/Controller/OrderedCollection.php @@ -4,6 +4,11 @@ declare(strict_types = 1); namespace Component\Collection\Util\Controller; -class OrderedCollection extends Collection +/** + * @template T + * + * @extends Collection + */ +abstract class OrderedCollection extends Collection { } diff --git a/components/Collection/Util/MetaCollectionTrait.php b/components/Collection/Util/MetaCollectionTrait.php index 12c983e127..572c7bce73 100644 --- a/components/Collection/Util/MetaCollectionTrait.php +++ b/components/Collection/Util/MetaCollectionTrait.php @@ -56,9 +56,9 @@ trait MetaCollectionTrait /** * create a collection owned by Actor $owner. * - * @param Actor $owner The collection's owner - * @param array $vars Page vars sent by AppendRightPanelBlock event - * @param string $name Collection's name + * @param Actor $owner The collection's owner + * @param array $vars Page vars sent by AppendRightPanelBlock event + * @param string $name Collection's name */ abstract protected function createCollection(Actor $owner, array $vars, string $name): void; /** @@ -82,6 +82,7 @@ trait MetaCollectionTrait /** * Check the route to determine whether the widget should be added + * * @param array $vars */ abstract protected function shouldAddToRightPanel(Actor $user, array $vars, Request $request): bool; @@ -91,7 +92,8 @@ trait MetaCollectionTrait * @param Actor $owner Collection's owner * @param null|array $vars Page vars sent by AppendRightPanelBlock event * @param bool $ids_only if true, the function must return only the primary key or each collections - * @return T[]|int[] + * + * @return int[]|T[] */ abstract protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array; @@ -101,7 +103,7 @@ trait MetaCollectionTrait * the current item to, and another to create a new collection. * * @param array $vars - * @param string[] $res + * @param string[] $res */ public function onAppendRightPanelBlock(Request $request, array $vars, array &$res): EventResult { @@ -146,7 +148,7 @@ trait MetaCollectionTrait if ($add_form->isSubmitted() && $add_form->isValid()) { $selected = $add_form->getData()['collections']; $removed = array_filter($already_selected, fn ($x) => !\in_array($x, $selected)); - $added = array_filter($selected, fn ($x) => !\in_array($x, $already_selected)); + $added = array_filter($selected, fn ($x) => !\in_array($x, $already_selected)); if (\count($removed) > 0) { $this->removeItem($user, $vars, $removed, $collections); } @@ -196,7 +198,7 @@ trait MetaCollectionTrait } /** - * @param string[] + * @param string[] $styles */ public function onEndShowStyles(array &$styles, string $route): EventResult { diff --git a/components/Collection/Util/Parser.php b/components/Collection/Util/Parser.php index 799b203f0c..80ab677417 100644 --- a/components/Collection/Util/Parser.php +++ b/components/Collection/Util/Parser.php @@ -32,6 +32,9 @@ abstract class Parser { /** * Merge $parts into $criteria_arr + * + * @param mixed[] $parts + * @param Criteria[] $criteria_arr */ private static function connectParts(array &$parts, array &$criteria_arr, string $last_op, mixed $eb, bool $force = false): void { diff --git a/components/Conversation/Controller/Conversation.php b/components/Conversation/Controller/Conversation.php index e387b11a5a..621af09fdb 100644 --- a/components/Conversation/Controller/Conversation.php +++ b/components/Conversation/Controller/Conversation.php @@ -46,6 +46,9 @@ use Component\Conversation\Entity\ConversationMute; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController<\App\Entity\Note> + */ class Conversation extends FeedController { /** @@ -55,7 +58,10 @@ class Conversation extends FeedController * * @throws \App\Util\Exception\ServerException * - * @return array Array containing keys: 'notes' (all known notes in the given Conversation), 'should_format' (boolean, stating if onFormatNoteList events may or not format given notes), 'page_title' (used as the title header) + * @return ControllerResultType Array containing keys: 'notes' (all known + * notes in the given Conversation), 'should_format' (boolean, stating if + * onFormatNoteList events may or not format given notes), 'page_title' + * (used as the title header) */ public function showConversation(Request $request, int $conversation_id): array { @@ -83,7 +89,7 @@ class Conversation extends FeedController * @throws NoSuchNoteException * @throws ServerException * - * @return array + * @return ControllerResultType */ public function addReply(Request $request) { @@ -103,7 +109,7 @@ class Conversation extends FeedController * @throws \App\Util\Exception\RedirectException * @throws \App\Util\Exception\ServerException * - * @return array Array containing templating where the form is to be rendered, and the form itself + * @return ControllerResultType Array containing templating where the form is to be rendered, and the form itself */ public function muteConversation(Request $request, int $conversation_id) { diff --git a/components/Conversation/Conversation.php b/components/Conversation/Conversation.php index 96bb283b7d..50ab9f6197 100644 --- a/components/Conversation/Conversation.php +++ b/components/Conversation/Conversation.php @@ -95,11 +95,13 @@ class Conversation extends Component * HTML rendering event that adds a reply link as a note * action, if a user is logged in. * - * @param \App\Entity\Note $note The Note being rendered - * @param array $actions Contains keys 'url' (linking 'conversation_reply_to' - * route), 'title' (used as title for aforementioned url), - * 'classes' (CSS styling classes used to visually inform the user of action context), - * 'id' (HTML markup id used to redirect user to this anchor upon performing the action) + * @param \App\Entity\Note $note The Note being rendered + * @param array{url: string, title: string, classes: string, id: string} $actions + * Contains keys 'url' (linking 'conversation_reply_to' route), + * 'title' (used as title for aforementioned url), 'classes' (CSS styling + * classes used to visually inform the user of action context), 'id' (HTML + * markup id used to redirect user to this anchor upon performing the + * action) * * @throws \App\Util\Exception\ServerException */ @@ -138,8 +140,11 @@ class Conversation extends Component /** * Append on note information about user actions. * - * @param array $vars Contains information related to Note currently being rendered - * @param array $result Contains keys 'actors', and 'action'. Needed to construct a string, stating who ($result['actors']), has already performed a reply ($result['action']), in the given Note (vars['note']) + * @param array $vars Contains information related to Note currently being rendered + * @param array{actors: Actor[], action: string} $result + *cContains keys 'actors', and 'action'. Needed to construct a string, + * stating who ($result['actors']), has already performed a reply + * ($result['action']), in the given Note (vars['note']) */ public function onAppendCardNote(array $vars, array &$result): EventResult { @@ -206,7 +211,7 @@ class Conversation extends Component /** * Posting event to add extra information to Component\Posting form data * - * @param array $data Transport data to be filled with reply_to_id + * @param array{reply_to_id: int} $data Transport data to be filled with reply_to_id * * @throws \App\Util\Exception\ClientException * @throws \App\Util\Exception\NoSuchNoteException @@ -224,6 +229,8 @@ class Conversation extends Component /** * Add minimal Note card to RightPanel template + * + * @param string[] $elements */ public function onPrependPostingForm(Request $request, array &$elements): EventResult { @@ -250,8 +257,10 @@ class Conversation extends Component /** * Adds extra actions related to Conversation Component, that act upon/from the given Note. * - * @param \App\Entity\Note $note Current Note being rendered - * @param array $actions Containing 'url' (Controller connected route), 'title' (used in anchor link containing the url), ?'classes' (CSS classes required for styling, if needed) + * @param \App\Entity\Note $note Current Note being rendered + * @param array{url: string, title: string, classes?: string} $actions Containing 'url' (Controller connected + * route), 'title' (used in anchor link containing the url), ?'classes' (CSS classes required for styling, if + * needed) * * @throws \App\Util\Exception\ServerException */ diff --git a/components/Feed/Controller/Feeds.php b/components/Feed/Controller/Feeds.php index 6025ddb391..80d321df9e 100644 --- a/components/Feed/Controller/Feeds.php +++ b/components/Feed/Controller/Feeds.php @@ -41,10 +41,15 @@ use App\Util\HTML\Heading; use Component\Collection\Util\Controller\FeedController; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController<\App\Entity\Note> + */ class Feeds extends FeedController { /** * The Planet feed represents every local post. Which is what this instance has to share with the universe. + * + * @return ControllerResultType */ public function public(Request $request): array { @@ -60,6 +65,8 @@ class Feeds extends FeedController /** * The Home feed represents everything that concerns a certain actor (its subscriptions) + * + * @return ControllerResultType */ public function home(Request $request): array { diff --git a/components/FreeNetwork/FreeNetwork.php b/components/FreeNetwork/FreeNetwork.php index 49e97358b1..6294dc647d 100644 --- a/components/FreeNetwork/FreeNetwork.php +++ b/components/FreeNetwork/FreeNetwork.php @@ -244,6 +244,8 @@ class FreeNetwork extends Component /** * Add a link header for LRDD Discovery + * + * @param mixed $action */ public function onStartShowHTML($action): EventResult { @@ -344,6 +346,7 @@ class FreeNetwork extends Component * @param string $preMention Character(s) that signals a mention ('@', '!'...) * * @return array the matching URLs (without @ or acct:) and each respective position in the given string + * * @example.com/mublog/user */ public static function extractUrlMentions(string $text, string $preMention = '@'): array @@ -375,6 +378,7 @@ class FreeNetwork extends Component * @param $mentions * * @return bool hook return value + * * @example.com/mublog/user */ public function onEndFindMentions(Actor $sender, string $text, array &$mentions): EventResult @@ -496,6 +500,9 @@ class FreeNetwork extends Component return Event::next; } + /** + * @param Actor[] $targets + */ public static function notify(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool { foreach (self::$protocols as $protocol) { @@ -517,6 +524,9 @@ class FreeNetwork extends Component /** * Add fediverse: query expression * // TODO: adding WebFinger would probably be nice + * + * @param mixed $note_expr + * @param mixed $actor_expr */ public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult { diff --git a/components/Group/Controller/Group.php b/components/Group/Controller/Group.php index 7cb0be7a6f..26b42bd5e9 100644 --- a/components/Group/Controller/Group.php +++ b/components/Group/Controller/Group.php @@ -61,6 +61,8 @@ class Group extends Controller * * @throws RedirectException * @throws ServerException + * + * @return ControllerResultType */ public function groupCreate(Request $request): array { @@ -89,6 +91,8 @@ class Group extends Controller * @throws NicknameTooLongException * @throws NotFoundException * @throws ServerException + * + * @return ControllerResultType */ public function groupSettings(Request $request, int $id): array { diff --git a/components/Group/Controller/GroupFeed.php b/components/Group/Controller/GroupFeed.php index f562b0848c..b788fcd4a9 100644 --- a/components/Group/Controller/GroupFeed.php +++ b/components/Group/Controller/GroupFeed.php @@ -40,10 +40,15 @@ use Component\Subscription\Entity\ActorSubscription; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController<\App\Entity\Note> + */ class GroupFeed extends FeedController { /** * @throws ServerException + * + * @return ControllerResultType */ public function groupView(Request $request, Actor $group): array { @@ -96,6 +101,8 @@ class GroupFeed extends FeedController /** * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function groupViewId(Request $request, int $id): array { @@ -119,6 +126,8 @@ class GroupFeed extends FeedController * * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function groupViewNickname(Request $request, string $nickname): array { diff --git a/components/Group/Group.php b/components/Group/Group.php index 4ab6fbd13e..b27afb275e 100644 --- a/components/Group/Group.php +++ b/components/Group/Group.php @@ -50,6 +50,8 @@ class Group extends Component /** * Enqueues a notification for an Actor (such as person or group) which means * it shows up in their home feed and such. + * + * @param Actor[] $targets */ public function onNewNotificationStart(Actor $sender, Activity $activity, array $targets = [], ?string $reason = null): EventResult { @@ -70,6 +72,9 @@ class Group extends Component /** * Add an to the profile card for groups, if the current actor can access them + * + * @param array $vars + * @param string[] $res */ public function onAppendCardProfile(array $vars, array &$res): EventResult { @@ -109,6 +114,9 @@ class Group extends Component return null; } + /** + * @param Actor[] $targets + */ public function onPostingFillTargetChoices(Request $request, Actor $actor, array &$targets): EventResult { $group = $this->getGroupFromContext($request); diff --git a/components/Language/Controller/Language.php b/components/Language/Controller/Language.php index 4940432b8c..ac32ddc406 100644 --- a/components/Language/Controller/Language.php +++ b/components/Language/Controller/Language.php @@ -70,7 +70,7 @@ class Language extends Controller ['actor_id' => $user->getId()], ); - $new_langs = array_udiff($selected_langs, $existing_langs, fn ($l, $r) => $l->getId() <=> $r->getId()); + $new_langs = array_udiff($selected_langs, $existing_langs, fn ($l, $r) => $l->getId() <=> $r->getId()); $removing_langs = array_udiff($existing_langs, $selected_langs, fn ($l, $r) => $l->getId() <=> $r->getId()); foreach ($new_langs as $l) { DB::persist(ActorLanguage::create(['actor_id' => $user->getId(), 'language_id' => $l->getId(), 'ordering' => 0])); @@ -100,6 +100,8 @@ class Language extends Controller * @throws NoLoggedInUser * @throws RedirectException * @throws ServerException + * + * @return ControllerResultType */ public function sortLanguages(Request $request): array { diff --git a/components/Language/Entity/ActorLanguage.php b/components/Language/Entity/ActorLanguage.php index 6ad172b8fe..b726cab069 100644 --- a/components/Language/Entity/ActorLanguage.php +++ b/components/Language/Entity/ActorLanguage.php @@ -119,6 +119,9 @@ class ActorLanguage extends Entity ) ?: [Language::getByLocale(Common::config('site', 'language'))]; } + /** + * @return int[] + */ public static function getActorRelatedLanguagesIds(Actor $actor): array { return Cache::getList( diff --git a/components/Language/Entity/Language.php b/components/Language/Entity/Language.php index 2e98c9a855..349fad2755 100644 --- a/components/Language/Entity/Language.php +++ b/components/Language/Entity/Language.php @@ -134,6 +134,9 @@ class Language extends Entity return self::getById($note->getLanguageId()); } + /** + * @return array + */ public static function getLanguageChoices(): array { $langs = Cache::getHashMap( @@ -144,6 +147,8 @@ class Language extends Entity return array_merge(...F\map(array_values($langs), fn ($l) => $l->toChoiceFormat())); } + /** + * @return array */ public function toChoiceFormat(): array { return [_m($this->getLongDisplay()) => $this->getLocale()]; @@ -152,6 +157,8 @@ class Language extends Entity /** * Get all the available languages as well as the languages $actor * prefers and are appropriate for posting in/to $context_actor + * + * @return array{array, array} */ public static function getSortedLanguageChoices(?Actor $actor, ?Actor $context_actor, ?bool $use_short_display): array { diff --git a/components/Language/Language.php b/components/Language/Language.php index a3cad0a2ca..ca026c8384 100644 --- a/components/Language/Language.php +++ b/components/Language/Language.php @@ -45,6 +45,9 @@ class Language extends Component return Event::next; } + /** + * @param Note[] $notes + */ public function onFilterNoteList(?Actor $actor, array &$notes, Request $request): EventResult { if (\is_null($actor)) { @@ -60,6 +63,9 @@ class Language extends Component /** * Populate $note_expr or $actor_expr with an expression to match a language + * + * @param mixed $note_expr + * @param mixed $actor_expr */ public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult { diff --git a/components/LeftPanel/LeftPanel.php b/components/LeftPanel/LeftPanel.php index 6b3e73af0e..dba25754cd 100644 --- a/components/LeftPanel/LeftPanel.php +++ b/components/LeftPanel/LeftPanel.php @@ -43,6 +43,8 @@ class LeftPanel extends Component } /** + * @param array $route_params + * * @throws \App\Util\Exception\DuplicateFoundException * @throws \App\Util\Exception\ServerException * @throws ClientException diff --git a/components/Link/Entity/NoteToLink.php b/components/Link/Entity/NoteToLink.php index 17fe7a8281..cdb038e43f 100644 --- a/components/Link/Entity/NoteToLink.php +++ b/components/Link/Entity/NoteToLink.php @@ -84,6 +84,8 @@ class NoteToLink extends Entity * Create an instance of NoteToLink or fill in the * properties of $obj with the associative array $args. Doesn't * persist the result + * + * @param (array{link_id: int, note_id: int} & array) $args */ public static function create(array $args, bool $_delegated_call = false): static { diff --git a/components/Link/Link.php b/components/Link/Link.php index 5afc61be0b..ec2bef4d39 100644 --- a/components/Link/Link.php +++ b/components/Link/Link.php @@ -54,6 +54,8 @@ class Link extends Component /** * Extract URLs from $content and create the appropriate Link and NoteToLink entities + * + * @param array{ignoreLinks?: string[]} $process_note_content_extra_args */ public function onProcessNoteContent(Note $note, string $content, string $content_type, array $process_note_content_extra_args = []): EventResult { @@ -150,7 +152,12 @@ class Link extends Component public const URL_SCHEME_NO_DOMAIN = 4; public const URL_SCHEME_COLON_COORDINATES = 8; - public function URLSchemes($filter = null) + /** + * @param self::URL_SCHEME_COLON_COORDINATES|self::URL_SCHEME_COLON_DOUBLE_SLASH|self::URL_SCHEME_NO_DOMAIN|self::URL_SCHEME_SINGLE_COLON $filter + * + * @return string[] + */ + public function URLSchemes(?int $filter = null): array { // TODO: move these to config $schemes = [ @@ -197,6 +204,7 @@ class Link extends Component * Intermediate callback for `replaceURLs()`, which helps resolve some * ambiguous link forms before passing on to the final callback. * + * @param string[] $matches * @param callable(string $text): string $callback: return replacement text */ private function callbackHelper(array $matches, callable $callback): string diff --git a/components/Notification/Controller/Feed.php b/components/Notification/Controller/Feed.php index a0c673c3ee..36536be00d 100644 --- a/components/Notification/Controller/Feed.php +++ b/components/Notification/Controller/Feed.php @@ -44,6 +44,8 @@ class Feed extends Controller { /** * Everything with attention to current user + * + * @return ControllerResultType */ public function notifications(Request $request): array { diff --git a/components/Notification/Entity/Notification.php b/components/Notification/Entity/Notification.php index 114b3f83c8..ad61c5461a 100644 --- a/components/Notification/Entity/Notification.php +++ b/components/Notification/Entity/Notification.php @@ -117,7 +117,7 @@ class Notification extends Entity /** * Pull the complete list of known activity context notifications for this activity. * - * @return array of integer actor ids (also group profiles) + * @return int[] actor ids (also group profiles) */ public static function getNotificationTargetIdsByActivity(int|Activity $activity_id): array { @@ -129,11 +129,17 @@ class Notification extends Entity return $targets; } + /** + * @return int[] + */ public function getNotificationTargetsByActivity(int|Activity $activity_id): array { return DB::findBy(Actor::class, ['id' => $this->getNotificationTargetIdsByActivity($activity_id)]); } + /** + * @return int[] + */ public static function getAllActivitiesTargetedAtActor(Actor $actor): array { return DB::dql(<<<'EOF' diff --git a/components/Notification/Notification.php b/components/Notification/Notification.php index 25ed3e62d7..833a83e705 100644 --- a/components/Notification/Notification.php +++ b/components/Notification/Notification.php @@ -71,10 +71,10 @@ class Notification extends Component * Example of $targets: * [42, $actor_alice, $actor_bob] // Avoid repeating actors or ids * - * @param Actor $sender The one responsible for this activity, take care not to include it in targets - * @param Activity $activity The activity responsible for the object being given to known to targets - * @param array $targets Attentions, Mentions, any other source. Should never be empty, you usually want to register an attention to every $sender->getSubscribers() - * @param null|string $reason An optional reason explaining why this notification exists + * @param Actor $sender The one responsible for this activity, take care not to include it in targets + * @param Activity $activity The activity responsible for the object being given to known to targets + * @param non-empty-array $targets Attentions, Mentions, any other source. Should never be empty, you usually want to register an attention to every $sender->getSubscribers() + * @param null|string $reason An optional reason explaining why this notification exists */ public function onNewNotification(Actor $sender, Activity $activity, array $targets, ?string $reason = null): EventResult { @@ -103,12 +103,19 @@ class Notification extends Component return Event::next; } + /** + * @param mixed[] $retry_args + */ public function onQueueNotificationLocal(Actor $sender, Activity $activity, Actor $target, ?string $reason, array &$retry_args): EventResult { // TODO: use https://symfony.com/doc/current/notifier.html return Event::stop; } + /** + * @param Actor[] $targets + * @param mixed[] $retry_args + */ public function onQueueNotificationRemote(Actor $sender, Activity $activity, array $targets, ?string $reason, array &$retry_args): EventResult { if (FreeNetwork::notify($sender, $activity, $targets, $reason)) { @@ -122,7 +129,9 @@ class Notification extends Component * Bring given Activity to Targets' knowledge. * This will flush a Notification to DB. * - * @return true if successful, false otherwise + * @param Actor[] $targets + * + * @return bool true if successful, false otherwise */ public static function notify(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool { @@ -134,7 +143,7 @@ class Notification extends Component continue; } if (Event::handle('NewNotificationShould', [$activity, $target]) === Event::next) { - if ($sender->getId() === $target->getId() + if ($sender->getId() === $target->getId() || $activity->getActorId() === $target->getId()) { // The target already knows about this, no need to bother with a notification continue; diff --git a/components/Person/Controller/PersonFeed.php b/components/Person/Controller/PersonFeed.php index e24d5bb80c..cc1be027ce 100644 --- a/components/Person/Controller/PersonFeed.php +++ b/components/Person/Controller/PersonFeed.php @@ -34,11 +34,16 @@ use App\Util\HTML\Heading; use Component\Collection\Util\Controller\FeedController; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController<\App\Entity\Note> + */ class PersonFeed extends FeedController { /** * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function personViewId(Request $request, int $id): array { @@ -62,6 +67,8 @@ class PersonFeed extends FeedController * * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function personViewNickname(Request $request, string $nickname): array { @@ -73,6 +80,9 @@ class PersonFeed extends FeedController return $this->personView($request, $person); } + /** + * @return ControllerResultType + */ public function personView(Request $request, Actor $person): array { return [ diff --git a/components/Person/Controller/PersonSettings.php b/components/Person/Controller/PersonSettings.php index da5f9543ae..e3b64863dd 100644 --- a/components/Person/Controller/PersonSettings.php +++ b/components/Person/Controller/PersonSettings.php @@ -88,6 +88,8 @@ class PersonSettings extends Controller * @throws NoLoggedInUser * @throws RedirectException * @throws ServerException + * + * @return ControllerResultType */ public function allSettings(Request $request, LanguageController $language): array { @@ -205,6 +207,8 @@ class PersonSettings extends Controller * @throws \Doctrine\DBAL\Exception * @throws NoLoggedInUser * @throws ServerException + * + * @return ControllerResultType[] */ private static function notifications(Request $request): array { @@ -251,7 +255,7 @@ class PersonSettings extends Controller // @codeCoverageIgnoreStart Log::critical("Structure of table user_notification_prefs changed in a way not accounted to in notification settings ({$name}): " . $type_str); throw new ServerException(_m('Internal server error')); - // @codeCoverageIgnoreEnd + // @codeCoverageIgnoreEnd } } diff --git a/components/Posting/Posting.php b/components/Posting/Posting.php index cac1e5e5f0..7ea22972bf 100644 --- a/components/Posting/Posting.php +++ b/components/Posting/Posting.php @@ -44,11 +44,13 @@ use App\Util\Exception\ServerException; use App\Util\Formatting; use App\Util\HTML; use Component\Attachment\Entity\ActorToAttachment; +use Component\Attachment\Entity\Attachment; use Component\Attachment\Entity\AttachmentToNote; use Component\Conversation\Conversation; use Component\Language\Entity\Language; use Component\Notification\Entity\Attention; use EventResult; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; @@ -66,6 +68,8 @@ class Posting extends Component * HTML render event handler responsible for adding and handling * the result of adding the note submission form, only if a user is logged in * + * @param array{post_form?: FormInterface} $res + * * @throws BugFoundException * @throws ClientException * @throws DuplicateFoundException @@ -84,9 +88,25 @@ class Posting extends Component } /** + * @param Actor $actor The Actor responsible for the creation of this Note + * @param null|string $content The raw text content + * @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...) + * @param null|string $locale Note's written text language, set by the default Actor language or upon filling + * @param null|VisibilityScope $scope The visibility of this Note + * @param Actor[]|int[] $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and target + * @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself + * @param UploadedFile[] $attachments UploadedFile[] to be stored as GSFiles associated to this note + * @param array $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note + * @param array{note?: Note, content?: string, content_type?: string, extra_args?: array} $process_note_content_extra_args Extra arguments for the event ProcessNoteContent + * @param bool $flush_and_notify True if the newly created Note activity should be passed on as a Notification + * @param null|string $rendered The Note's content post RenderNoteContent event, which sanitizes and processes the raw content sent + * @param string $source The source of this Note + * * @throws ClientException * @throws DuplicateFoundException * @throws ServerException + * + * @return array{\App\Entity\Activity, \App\Entity\Note, array} */ public static function storeLocalArticle( Actor $actor, @@ -144,25 +164,25 @@ class Posting extends Component * $actor_id, possibly as a reply to note $reply_to and with flag * $is_local. Sanitizes $content and $attachments * - * @param Actor $actor The Actor responsible for the creation of this Note - * @param null|string $content The raw text content - * @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...) - * @param null|string $locale Note's written text language, set by the default Actor language or upon filling - * @param null|VisibilityScope $scope The visibility of this Note - * @param array $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and target - * @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself - * @param array $attachments UploadedFile[] to be stored as GSFiles associated to this note - * @param array $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note - * @param array $process_note_content_extra_args Extra arguments for the event ProcessNoteContent - * @param bool $flush_and_notify True if the newly created Note activity should be passed on as a Notification - * @param null|string $rendered The Note's content post RenderNoteContent event, which sanitizes and processes the raw content sent - * @param string $source The source of this Note + * @param Actor $actor The Actor responsible for the creation of this Note + * @param null|string $content The raw text content + * @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...) + * @param null|string $locale Note's written text language, set by the default Actor language or upon filling + * @param null|VisibilityScope $scope The visibility of this Note + * @param Actor[]|int[] $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and targte + * @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself + * @param UploadedFile[] $attachments UploadedFile[] to be stored as GSFiles associated to this note + * @param array $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note + * @param array{note?: Note, content?: string, content_type?: string, extra_args?: array} $process_note_content_extra_args Extra arguments for the event ProcessNoteContent + * @param bool $flush_and_notify True if the newly created Note activity should be passed on as a Notification + * @param null|string $rendered The Note's content post RenderNoteContent event, which sanitizes and processes the raw content sent + * @param string $source The source of this Note * * @throws ClientException * @throws DuplicateFoundException * @throws ServerException * - * @return array [Activity, Note, Effective Attentions] + * @return array{\App\Entity\Activity, \App\Entity\Note, array} */ public static function storeLocalNote( Actor $actor, @@ -302,6 +322,9 @@ class Posting extends Component return [$activity, $note, $effective_attentions]; } + /** + * @param array $mentions + */ public function onRenderNoteContent(string $content, string $content_type, ?string &$rendered, Actor $author, ?string $language = null, array &$mentions = []): EventResult { switch ($content_type) { diff --git a/components/Search/Controller/Search.php b/components/Search/Controller/Search.php index f7a1b26d03..f56d8c47f9 100644 --- a/components/Search/Controller/Search.php +++ b/components/Search/Controller/Search.php @@ -38,12 +38,17 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController<\App\Entity\Note> + */ class Search extends FeedController { /** * Handle a search query + * + * @return ControllerResultType */ - public function handle(Request $request) + public function handle(Request $request): array { $actor = Common::actor(); $language = !\is_null($actor) ? $actor->getTopLanguage()->getLocale() : null; diff --git a/components/Search/Search.php b/components/Search/Search.php index 279f652af4..3ead7818c3 100644 --- a/components/Search/Search.php +++ b/components/Search/Search.php @@ -134,6 +134,8 @@ class Search extends Component /** * Add the search form to the site header * + * @param string[] $elements + * * @throws RedirectException */ public function onPrependRightPanelBlock(Request $request, array &$elements): EventResult @@ -145,7 +147,7 @@ class Search extends Component /** * Output our dedicated stylesheet * - * @param array $styles stylesheets path + * @param string[] $styles stylesheets path */ public function onEndShowStyles(array &$styles, string $route): EventResult { diff --git a/components/Subscription/Controller/Subscribers.php b/components/Subscription/Controller/Subscribers.php index afb6ac516a..079b5e6bf5 100644 --- a/components/Subscription/Controller/Subscribers.php +++ b/components/Subscription/Controller/Subscribers.php @@ -45,6 +45,8 @@ class Subscribers extends CircleController { /** * @throws ServerException + * + * @return ControllerResultType */ public function subscribersByActor(Request $request, Actor $actor): array { @@ -61,6 +63,8 @@ class Subscribers extends CircleController /** * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function subscribersByActorId(Request $request, int $id): array { @@ -78,6 +82,8 @@ class Subscribers extends CircleController * @throws \App\Util\Exception\ServerException * @throws ClientException * @throws RedirectException + * + * @return ControllerResultType */ public function subscribersAdd(Request $request, int $object_id): array { @@ -126,6 +132,8 @@ class Subscribers extends CircleController * @throws \App\Util\Exception\ServerException * @throws ClientException * @throws RedirectException + * + * @return ControllerResultType */ public function subscribersRemove(Request $request, int $object_id): array { diff --git a/components/Subscription/Controller/Subscriptions.php b/components/Subscription/Controller/Subscriptions.php index 3e6fc17a75..112f0f2f1a 100644 --- a/components/Subscription/Controller/Subscriptions.php +++ b/components/Subscription/Controller/Subscriptions.php @@ -38,6 +38,8 @@ class Subscriptions extends CircleController /** * @throws ClientException * @throws ServerException + * + * @return ControllerResultType */ public function subscriptionsByActorId(Request $request, int $id): array { @@ -48,7 +50,10 @@ class Subscriptions extends CircleController return $this->subscriptionsByActor($request, $actor); } - public function subscriptionsByActor(Request $request, Actor $actor) + /** + * @return ControllerResultType + */ + public function subscriptionsByActor(Request $request, Actor $actor): array { return [ '_template' => 'collection/actors.html.twig', diff --git a/components/Subscription/Subscription.php b/components/Subscription/Subscription.php index 09ec67f907..893c37f9c2 100644 --- a/components/Subscription/Subscription.php +++ b/components/Subscription/Subscription.php @@ -58,6 +58,8 @@ class Subscription extends Component * * @param Actor|int|LocalUser $subject The Actor who subscribed or unsubscribed * @param Actor|int|LocalUser $object The Actor who was subscribed or unsubscribed from + * + * @return array{bool, bool} */ public static function refreshSubscriptionCount(int|Actor|LocalUser $subject, int|Actor|LocalUser $object): array { @@ -177,9 +179,10 @@ class Subscription extends Component * In the case of ``\App\Component\Subscription``, the action added allows a **LocalUser** to **subscribe** or * **unsubscribe** a given **Actor**. * - * @param Actor $object The Actor on which the action is to be performed - * @param array $actions An array containing all actions added to the - * current profile, this event adds an action to it + * @param Actor $object The Actor on which the action is to be performed + * @param array $actions + * An array containing all actions added to the + * current profile, this event adds an action to it * * @throws DuplicateFoundException * @throws NotFoundException diff --git a/components/Tag/Controller/Tag.php b/components/Tag/Controller/Tag.php index b93f9d211b..3a5b16c71d 100644 --- a/components/Tag/Controller/Tag.php +++ b/components/Tag/Controller/Tag.php @@ -15,7 +15,12 @@ class Tag extends Controller // TODO: Use Feed::query // TODO: If ?canonical=something, respect // TODO: Allow to set locale of tag being selected - private function process(null|string|array $tag_single_or_multi, string $key, string $query, string $template, bool $include_locale = false) + /** + * @param (null|string|string[]) $tag_single_or_multi + * + * @return ControllerResultType + */ + private function process(null|string|array $tag_single_or_multi, string $key, string $query, string $template, bool $include_locale = false): array { $actor = Common::actor(); $page = $this->int('page') ?: 1; @@ -46,7 +51,10 @@ class Tag extends Controller ]; } - public function single_note_tag(string $tag) + /** + * @return ControllerResultType + */ + public function single_note_tag(string $tag): array { return $this->process( tag_single_or_multi: $tag, @@ -57,7 +65,10 @@ class Tag extends Controller ); } - public function multi_note_tags(string $tags) + /** + * @return ControllerResultType + */ + public function multi_note_tags(string $tags): array { return $this->process( tag_single_or_multi: explode(',', $tags), diff --git a/components/Tag/Entity/NoteTag.php b/components/Tag/Entity/NoteTag.php index d51ac5325a..713829d94f 100644 --- a/components/Tag/Entity/NoteTag.php +++ b/components/Tag/Entity/NoteTag.php @@ -134,6 +134,9 @@ class NoteTag extends Entity return "note-tags-{$note_id}"; } + /** + * @return NoteTag[] + */ public static function getByNoteId(int $note_id): array { return Cache::getList(self::cacheKey($note_id), fn () => DB::dql('SELECT nt FROM note_tag AS nt JOIN note AS n WITH n.id = nt.note_id WHERE n.id = :id', ['id' => $note_id])); diff --git a/components/Tag/Entity/NoteTagBlock.php b/components/Tag/Entity/NoteTagBlock.php index c356ece251..3d9e1b9764 100644 --- a/components/Tag/Entity/NoteTagBlock.php +++ b/components/Tag/Entity/NoteTagBlock.php @@ -119,6 +119,8 @@ class NoteTagBlock extends Entity /** * Check whether $note_tag is considered blocked by one of * $note_tag_blocks + * + * @param NoteTagBlock[] $note_tag_blocks */ public static function checkBlocksNoteTag(NoteTag $note_tag, array $note_tag_blocks): bool { diff --git a/components/Tag/Tag.php b/components/Tag/Tag.php index a0f97ec7ea..db6108dd54 100644 --- a/components/Tag/Tag.php +++ b/components/Tag/Tag.php @@ -61,13 +61,16 @@ class Tag extends Component public const TAG_REGEX = '/(^|\\s)(#[\\pL\\pN_\\-]{1,64})/u'; // Brion Vibber 2011-02-23 v2:classes/Notice.php:367 function saveTags public const TAG_SLUG_REGEX = '[A-Za-z0-9]{1,64}'; - public function onAddRoute($r): EventResult + public function onAddRoute(Router $r): EventResult { $r->connect('single_note_tag', '/note-tag/{tag<' . self::TAG_SLUG_REGEX . '>}', [Controller\Tag::class, 'single_note_tag']); $r->connect('multi_note_tags', '/note-tags/{tags<(' . self::TAG_SLUG_REGEX . ',)+' . self::TAG_SLUG_REGEX . '>}', [Controller\Tag::class, 'multi_note_tags']); return Event::next; } + /** + * @param array{tag_use_canonical?: bool} $extra_args + */ public static function maybeCreateTag(string $tag, int $note_id, ?int $lang_id, array $extra_args = []): ?NoteTag { if (!self::validate($tag)) { @@ -118,6 +121,8 @@ class Tag extends Component /** * Process note by extracting any tags present + * + * @param array{TagProcessed?: bool} $extra_args */ public function onProcessNoteContent(Note $note, string $content, string $content_type, array $extra_args): EventResult { @@ -213,6 +218,9 @@ class Tag extends Component * Populate $note_expr with an expression to match a tag, if the term looks like a tag * * $term /^(note|tag|people|actor)/ means we want to match only either a note or an actor + * + * @param mixed $note_expr + * @param mixed $actor_expr */ public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult { @@ -264,12 +272,19 @@ class Tag extends Component return Event::next; } + /** + * @param array{string, class-string, array} $form_params + */ public function onPostingAddFormEntries(Request $request, Actor $actor, array &$form_params): EventResult { $form_params[] = ['tag_use_canonical', CheckboxType::class, ['required' => false, 'data' => true, 'label' => _m('Make note tags canonical'), 'help' => _m('Canonical tags will be treated as a version of an existing tag with the same root/stem (e.g. \'#great_tag\' will be considered as a version of \'#great\', if it already exists)')]]; return Event::next; } + /** + * @param array{tag_use_canonical?: bool} $data + * @param array{tag_use_canonical?: bool} $extra_args + */ public function onAddExtraArgsToNoteContent(Request $request, Actor $actor, array $data, array &$extra_args): EventResult { if (!isset($data['tag_use_canonical'])) { diff --git a/phpstan.neon b/phpstan.neon index b74751a8a7..9d5cbd048c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,7 @@ parameters: - level: 5 + level: 6 + tmpDir: /var/www/social/var/cache/phpstan + inferPrivatePropertyTypeFromConstructor: true bootstrapFiles: - config/bootstrap.php paths: @@ -16,10 +18,11 @@ parameters: App\Core\Log: - unexpected_exception typeAliases: - ControllerResultType: 'array{_template: string} & array' + ControllerResultType: '(array{_template: string} | array{_redirect: string}) & array' CacheKeysType: 'array' SettingsTabsType: 'array' OrderByType: "'ASC' | 'DESC' | 'asc' | 'desc'" + ModuleVersionType: 'array{name: string, version: string, author: string, rawdescription: string}' ignoreErrors: - @@ -41,10 +44,41 @@ parameters: paths: - * - # - - # message: '/has no return typehint specified/' - # paths: - # - tests/* + - + message: '/::onCollectionQueryCreateExpression\(\) has parameter \$(actor|note)_expr with no type specified\./' + paths: + - * + + - + message: '/::schemaDef\(\) return type has no value type specified in iterable type array\./' + paths: + - * + + - + message: '/has no return type specified\./' + paths: + - * + + - + message: '/with no value type specified in iterable type (array|iterable)\.|type has no value type specified in iterable type (array|iterable)\./' + paths: + - * + + - + message: '/never returns array{_redirect: string} so it can be removed from the return type\./' + paths: + - * + + - + message: '/but returns array\|string>\./' + paths: + - plugins/AttachmentCollections/Controller/AttachmentCollections.php + - plugins/Bundles/Controller/BundleCollection.php + + - + message: '/has parameter \$.+ with no type specified./' + paths: + - tests/* services: - diff --git a/plugins/AttachmentCollections/AttachmentCollections.php b/plugins/AttachmentCollections/AttachmentCollections.php index f9662afdbd..958f40262d 100644 --- a/plugins/AttachmentCollections/AttachmentCollections.php +++ b/plugins/AttachmentCollections/AttachmentCollections.php @@ -49,9 +49,14 @@ use Symfony\Component\HttpFoundation\Request; class AttachmentCollections extends Plugin { + /** @phpstan-use MetaCollectionTrait */ use MetaCollectionTrait; protected const SLUG = 'collection'; protected const PLURAL_SLUG = 'collections'; + + /** + * @param array $vars + */ protected function createCollection(Actor $owner, array $vars, string $name): void { $col = AttachmentCollection::create([ @@ -65,6 +70,12 @@ class AttachmentCollections extends Plugin 'attachment_collection_id' => $col->getId(), ])); } + + /** + * @param array $vars + * @param int[] $items + * @param array $collections + */ protected function removeItem(Actor $owner, array $vars, array $items, array $collections): bool { return DB::dql(<<<'EOF' @@ -83,6 +94,11 @@ class AttachmentCollections extends Plugin ]); } + /** + * @param array $vars + * @param int[] $items + * @param array $collections + */ protected function addItem(Actor $owner, array $vars, array $items, array $collections): void { foreach ($items as $id) { @@ -97,14 +113,18 @@ class AttachmentCollections extends Plugin } } - protected function shouldAddToRightPanel(Actor $user, $vars, Request $request): bool + /** + * @param array $vars + */ + protected function shouldAddToRightPanel(Actor $user, array $vars, Request $request): bool { return $vars['path'] === 'note_attachment_show'; } /** * @param array $vars - * @return int[] + * + * @return ($ids_only is true ? int[] : AttachmentCollection[]) */ protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array { diff --git a/plugins/AttachmentCollections/Controller/AttachmentCollections.php b/plugins/AttachmentCollections/Controller/AttachmentCollections.php index f589a4fd6f..3928e9ceb5 100644 --- a/plugins/AttachmentCollections/Controller/AttachmentCollections.php +++ b/plugins/AttachmentCollections/Controller/AttachmentCollections.php @@ -28,6 +28,9 @@ use App\Core\Router; use Component\Collection\Util\Controller\MetaCollectionController; use Plugin\AttachmentCollections\Entity\AttachmentCollection; +/** + * @extends MetaCollectionController + */ class AttachmentCollections extends MetaCollectionController { public function createCollection(int $owner_id, string $name): bool @@ -36,6 +39,8 @@ class AttachmentCollections extends MetaCollectionController 'name' => $name, 'actor_id' => $owner_id, ])); + + return true; } public function getCollectionUrl(int $owner_id, ?string $owner_nickname, int $collection_id): string @@ -51,7 +56,12 @@ class AttachmentCollections extends MetaCollectionController ['nickname' => $owner_nickname, 'cid' => $collection_id], ); } - public function getCollectionItems(int $owner_id, $collection_id): array + + /** + * FIXME return value not consistent with base class + */ + // @phpstan-disable-next-line + public function getCollectionItems(int $owner_id, int $collection_id): array { [$attachs, $notes] = DB::dql( <<<'EOF' @@ -64,28 +74,30 @@ class AttachmentCollections extends MetaCollectionController EOF, ['cid' => $collection_id], ); - return [ - '_template' => 'AttachmentCollections/collection_entry_view.html.twig', - 'attachments' => array_values($attachs), - 'bare_notes' => array_values($notes), - ]; + + return ['_template' => 'AttachmentCollections/collection_entry_view.html.twig', 'attachments' => array_values($attachs), 'bare_notes' => array_values($notes)]; } + + /** + * @return AttachmentCollection[] + */ public function getCollectionsByActorId(int $owner_id): array { return DB::findBy(AttachmentCollection::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']); } + public function getCollectionBy(int $owner_id, int $collection_id): AttachmentCollection { return DB::findOneBy(AttachmentCollection::class, ['id' => $collection_id]); } - public function setCollectionName(int $actor_id, string $actor_nickname, AttachmentCollection $collection, string $name) + public function setCollectionName(int $actor_id, string $actor_nickname, AttachmentCollection $collection, string $name): void { $collection->setName($name); DB::persist($collection); } - public function removeCollection(int $actor_id, string $actor_nickname, AttachmentCollection $collection) + public function removeCollection(int $actor_id, string $actor_nickname, AttachmentCollection $collection): void { DB::remove($collection); } diff --git a/plugins/AttachmentShowRelated/AttachmentShowRelated.php b/plugins/AttachmentShowRelated/AttachmentShowRelated.php index 8e2747b2e4..26730945b6 100644 --- a/plugins/AttachmentShowRelated/AttachmentShowRelated.php +++ b/plugins/AttachmentShowRelated/AttachmentShowRelated.php @@ -35,7 +35,7 @@ class AttachmentShowRelated extends Plugin { /** * @param array $vars - * @param string[] $res + * @param string[] $res */ public function onAppendRightPanelBlock(Request $request, array $vars, array &$res): EventResult { @@ -55,7 +55,7 @@ class AttachmentShowRelated extends Plugin /** * Output our dedicated stylesheet * - * @param array $styles stylesheets path + * @param string[] $styles stylesheets path */ public function onEndShowStyles(array &$styles, string $path): EventResult { diff --git a/plugins/AudioEncoder/AudioEncoder.php b/plugins/AudioEncoder/AudioEncoder.php index 9280929b9d..b61bce5ed8 100644 --- a/plugins/AudioEncoder/AudioEncoder.php +++ b/plugins/AudioEncoder/AudioEncoder.php @@ -54,6 +54,9 @@ class AudioEncoder extends Plugin return GSFile::mimetypeMajor($mimetype) === 'audio'; } + /** + * @param array $event_map + */ public function onFileMetaAvailable(array &$event_map, string $mimetype): EventResult { if (!self::shouldHandle($mimetype)) { @@ -90,6 +93,9 @@ class AudioEncoder extends Plugin /** * Generates the view for attachments of type Video + * + * @param (array{attachment: \Component\Attachment\Entity\Attachment, note: \App\Entity\Note, title: string} & array) $vars + * @param array $res */ public function onViewAttachment(array $vars, array &$res): EventResult { @@ -109,6 +115,8 @@ class AudioEncoder extends Plugin } /** + * @param ModuleVersionType[] $versions + * * @throws ServerException */ public function onPluginVersion(array &$versions): EventResult diff --git a/plugins/Blog/Blog.php b/plugins/Blog/Blog.php index d30ee52b76..89c41cfcef 100644 --- a/plugins/Blog/Blog.php +++ b/plugins/Blog/Blog.php @@ -39,6 +39,10 @@ class Blog extends Plugin return Event::next; } + /** + * @param (array{actor: \App\Entity\Actor} & array) $vars + * @param array $res + */ public function onAppendCardProfile(array $vars, array &$res): EventResult { $actor = Common::actor(); diff --git a/plugins/Bundles/Bundles.php b/plugins/Bundles/Bundles.php index 96e21f89fe..c8ef0454cb 100644 --- a/plugins/Bundles/Bundles.php +++ b/plugins/Bundles/Bundles.php @@ -32,6 +32,9 @@ use Symfony\Component\HttpFoundation\Request; class Bundles extends Plugin { + /** + * @phpstan-use MetaCollectionTrait + */ use MetaCollectionTrait; protected const SLUG = 'bundle'; protected const PLURAL_SLUG = 'bundles'; @@ -54,7 +57,8 @@ class Bundles extends Plugin /** * @param array $vars - * @param array $items + * @param array $items + * @param array $collections */ protected function removeItem(Actor $owner, array $vars, array $items, array $collections): bool { @@ -75,8 +79,8 @@ class Bundles extends Plugin /** * @param array $vars - * @param array $items - * @param array $collections + * @param array $items + * @param array $collections */ protected function addItem(Actor $owner, array $vars, array $items, array $collections): void { @@ -101,8 +105,11 @@ class Bundles extends Plugin } /** - * @param array $vars - * @retrun int[] + * FIXME incompatible return type + * + * @param null|array $vars + * + * @return BundleCollection[]|int[] */ protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array { diff --git a/plugins/Bundles/Controller/BundleCollections.php b/plugins/Bundles/Controller/BundleCollection.php similarity index 81% rename from plugins/Bundles/Controller/BundleCollections.php rename to plugins/Bundles/Controller/BundleCollection.php index 083001c103..b43ac81ba8 100644 --- a/plugins/Bundles/Controller/BundleCollections.php +++ b/plugins/Bundles/Controller/BundleCollection.php @@ -26,9 +26,12 @@ namespace Plugin\Bundles\Controller; use App\Core\DB; use App\Core\Router; use Component\Collection\Util\Controller\MetaCollectionController; -use Plugin\Bundles\Entity\BundleCollection; +use Plugin\Bundles\Entity\BundleCollection as BundleCollectionEntity; -class BundleCollections extends MetaCollectionController +/** + * @extends MetaCollectionController + */ +class BundleCollection extends MetaCollectionController { public function getCollectionUrl(int $owner_id, ?string $owner_nickname, int $collection_id): string { @@ -44,7 +47,8 @@ class BundleCollections extends MetaCollectionController ); } - public function getCollectionItems(int $owner_id, $collection_id): array + // FIXME + public function getCollectionItems(int $owner_id, int $collection_id): array { [$notes] = DB::dql( <<<'EOF' @@ -65,17 +69,17 @@ class BundleCollections extends MetaCollectionController public function getCollectionsByActorId(int $owner_id): array { - return DB::findBy(BundleCollection::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']); + return DB::findBy(BundleCollectionEntity::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']); } - public function getCollectionBy(int $owner_id, int $collection_id): BundleCollection + public function getCollectionBy(int $owner_id, int $collection_id): BundleCollectionEntity { - return DB::findOneBy(BundleCollection::class, ['id' => $collection_id]); + return DB::findOneBy(BundleCollectionEntity::class, ['id' => $collection_id]); } public function createCollection(int $owner_id, string $name): bool { - DB::persist(BundleCollection::create([ + DB::persist(BundleCollectionEntity::create([ 'name' => $name, 'actor_id' => $owner_id, ])); diff --git a/plugins/Cover/Controller/Cover.php b/plugins/Cover/Controller/Cover.php index 16e18493e6..eaf54f77ce 100644 --- a/plugins/Cover/Controller/Cover.php +++ b/plugins/Cover/Controller/Cover.php @@ -58,7 +58,7 @@ class Cover * @throws ClientException Invalid form * @throws ServerException Invalid file type * - * @return array template + * @return ControllerResultType */ public static function coverSettings(Request $request): array { @@ -113,7 +113,7 @@ class Cover // Only delete files if the commit went through if ($old_file != null) { foreach ($old_file as $f) { - @unlink($f); + @unlink($f->getPath()); } } throw new RedirectException(); @@ -131,7 +131,7 @@ class Cover DB::remove($cover); DB::flush(); foreach ($old_file as $f) { - @unlink($f); + @unlink($f->getPath()); } throw new RedirectException(); } diff --git a/plugins/Cover/Cover.php b/plugins/Cover/Cover.php index ffdc783403..f638a43f43 100644 --- a/plugins/Cover/Cover.php +++ b/plugins/Cover/Cover.php @@ -90,7 +90,7 @@ class Cover extends Plugin /** * Output our dedicated stylesheet * - * @param array $styles stylesheets path + * @param string[] $styles stylesheets path */ public function onStartShowStyles(array &$styles): EventResult { diff --git a/plugins/Cover/Entity/Cover.php b/plugins/Cover/Entity/Cover.php index 03da189574..ae6f2088e4 100644 --- a/plugins/Cover/Entity/Cover.php +++ b/plugins/Cover/Entity/Cover.php @@ -117,7 +117,7 @@ class Cover extends Entity /** * Delete this cover and the corresponding attachment and thumbnails, which this owns * - * @return array attachments deleted (if delete_attachments_now is true) + * @return Attachment[] attachments deleted (if delete_attachments_now is true) */ public function delete(bool $flush = false, bool $delete_attachments_now = false, bool $cascading = false): array { diff --git a/plugins/DeleteNote/DeleteNote.php b/plugins/DeleteNote/DeleteNote.php index 2139d2ff55..323bb365f9 100644 --- a/plugins/DeleteNote/DeleteNote.php +++ b/plugins/DeleteNote/DeleteNote.php @@ -150,6 +150,8 @@ class DeleteNote extends NoteHandlerPlugin * @throws \App\Util\Exception\DuplicateFoundException * @throws \App\Util\Exception\NotFoundException * @throws \App\Util\Exception\ServerException + * + * @params string[] $actions */ public function onAddExtraNoteActions(Request $request, Note $note, array &$actions): EventResult { diff --git a/plugins/EmailNotifications/EmailNotifications.php b/plugins/EmailNotifications/EmailNotifications.php index 2ad1f3de53..e9a41a1f91 100644 --- a/plugins/EmailNotifications/EmailNotifications.php +++ b/plugins/EmailNotifications/EmailNotifications.php @@ -40,7 +40,7 @@ use EventResult; class EmailNotifications extends Plugin { - public function onAddNotificationTransport(&$form_defs): EventResult + public function onAddNotificationTransport(array &$form_defs): EventResult { $form_defs['Email'] = $form_defs['placeholder']; $form_defs['Email'][] = $form_defs['placeholder']['save']('Email', 'save_email'); diff --git a/plugins/Embed/Controller/OEmbed.php b/plugins/Embed/Controller/OEmbed.php index d8a34fa340..392a63ed69 100644 --- a/plugins/Embed/Controller/OEmbed.php +++ b/plugins/Embed/Controller/OEmbed.php @@ -216,7 +216,7 @@ class OEmbed extends Controller /** * Placeholder */ - public function init_document($type) + public function init_document(string $type): void { throw new NotImplementedException; // switch ($type) { @@ -243,7 +243,7 @@ class OEmbed extends Controller /** * Placeholder */ - public function end_document($type) + public function end_document(string $type): void { throw new NotImplementedException; // switch ($type) { diff --git a/plugins/Favourite/Controller/Favourite.php b/plugins/Favourite/Controller/Favourite.php index 99999ee4d0..86f0181110 100644 --- a/plugins/Favourite/Controller/Favourite.php +++ b/plugins/Favourite/Controller/Favourite.php @@ -26,6 +26,7 @@ namespace Plugin\Favourite\Controller; use App\Core\DB; use App\Core\Event; use App\Core\Form; +use function App\Core\I18n\_m; use App\Core\Log; use App\Core\Router; use App\Entity\Activity; @@ -41,10 +42,12 @@ use App\Util\Exception\RedirectException; use App\Util\Exception\ServerException; use Component\Collection\Util\Controller\FeedController; use Component\Notification\Entity\Attention; -use function App\Core\I18n\_m; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController + */ class Favourite extends FeedController { /** diff --git a/plugins/LatexNotes/LatexNotes.php b/plugins/LatexNotes/LatexNotes.php index 35d8243451..01ba6e0ee7 100644 --- a/plugins/LatexNotes/LatexNotes.php +++ b/plugins/LatexNotes/LatexNotes.php @@ -44,7 +44,8 @@ class LatexNotes extends Plugin $types['LaTeX'] = 'application/x-latex'; return Event::next; } - public function onRenderNoteContent($content, $content_type, &$rendered): EventResult + + public function onRenderNoteContent(string $content, string $content_type, string &$rendered): EventResult { if ($content_type !== 'application/x-latex') { return Event::next; diff --git a/plugins/MarkdownNotes/MarkdownNotes.php b/plugins/MarkdownNotes/MarkdownNotes.php index 1ad50d9af3..bc8f76ba43 100644 --- a/plugins/MarkdownNotes/MarkdownNotes.php +++ b/plugins/MarkdownNotes/MarkdownNotes.php @@ -43,7 +43,8 @@ class MarkdownNotes extends Plugin $types['Markdown'] = 'text/markdown'; return Event::next; } - public function onRenderNoteContent($content, $content_type, &$rendered): EventResult + + public function onRenderNoteContent(string $content, string $content_type, string &$rendered): EventResult { if ($content_type !== 'text/markdown') { return Event::next; diff --git a/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php b/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php index ba1d386161..65053419e9 100644 --- a/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php +++ b/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php @@ -140,7 +140,7 @@ class NoteTypeFeedFilter extends Plugin /** * Draw the media feed navigation. */ - public function onAddFeedActions(Request $request, bool $is_not_empty, &$res): EventResult + public function onAddFeedActions(Request $request, bool $is_not_empty, array &$res): EventResult { $qs = []; $query_string = $request->getQueryString(); diff --git a/plugins/OAuth2/Entity/AccessToken.php b/plugins/OAuth2/Entity/AccessToken.php index bb68c2eff5..aa94defd2d 100644 --- a/plugins/OAuth2/Entity/AccessToken.php +++ b/plugins/OAuth2/Entity/AccessToken.php @@ -36,6 +36,9 @@ use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use Plugin\OAuth2\Util\Token; +/** + * @extends Token + */ class AccessToken extends Token implements AccessTokenEntityInterface { // {{{ Autocode diff --git a/plugins/OAuth2/Entity/AuthCode.php b/plugins/OAuth2/Entity/AuthCode.php index 0e9b8415b3..e545babce6 100644 --- a/plugins/OAuth2/Entity/AuthCode.php +++ b/plugins/OAuth2/Entity/AuthCode.php @@ -36,6 +36,9 @@ use League\OAuth2\Server\Entities\AuthCodeEntityInterface; use Plugin\OAuth2\Repository; use Plugin\OAuth2\Util\Token; +/** + * @extends Token + */ class AuthCode extends Token implements AuthCodeEntityInterface { // {{{ Autocode diff --git a/plugins/OAuth2/Repository/RefreshToken.php b/plugins/OAuth2/Repository/RefreshToken.php index 8b1233efbd..efc9c19a1e 100644 --- a/plugins/OAuth2/Repository/RefreshToken.php +++ b/plugins/OAuth2/Repository/RefreshToken.php @@ -45,11 +45,17 @@ class RefreshToken implements RefreshTokenRepositoryInterface DB::persist($refreshTokenEntity); } + /** + * @param string $tokenId + */ public function revokeRefreshToken($tokenId) { // Some logic to revoke the auth token in a database } + /** + * @param string $tokenId + */ public function isRefreshtokenRevoked($tokenId): bool { return false; // The auth token has not been revoked diff --git a/plugins/PinnedNotes/Controller/PinnedNotes.php b/plugins/PinnedNotes/Controller/PinnedNotes.php index 0ee6b20f06..428eeb50b7 100644 --- a/plugins/PinnedNotes/Controller/PinnedNotes.php +++ b/plugins/PinnedNotes/Controller/PinnedNotes.php @@ -29,6 +29,7 @@ use function App\Core\I18n\_m; use App\Core\Router; use App\Entity\Actor; use App\Entity\LocalUser; +use App\Entity\Note; use App\Util\Common; use App\Util\Exception\ClientException; use App\Util\Exception\NoSuchNoteException; @@ -39,6 +40,9 @@ use Plugin\PinnedNotes\Entity as E; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +/** + * @extends FeedController + */ class PinnedNotes extends FeedController { public function listPinsByNickname(Request $request, string $nickname) diff --git a/plugins/PinnedNotes/Entity/PinnedNotes.php b/plugins/PinnedNotes/Entity/PinnedNotes.php index 13d23f9996..f6ff9ef7a4 100644 --- a/plugins/PinnedNotes/Entity/PinnedNotes.php +++ b/plugins/PinnedNotes/Entity/PinnedNotes.php @@ -16,7 +16,8 @@ class PinnedNotes extends Entity { return $this->id; } - public function setId($id) + + public function setId(int $id): self { $this->id = $id; return $this; @@ -26,7 +27,8 @@ class PinnedNotes extends Entity { return $this->actor_id; } - public function setActorId($actor_id) + + public function setActorId(int $actor_id): self { $this->actor_id = $actor_id; return $this; @@ -36,7 +38,8 @@ class PinnedNotes extends Entity { return $this->note_id; } - public function setNoteId($note_id) + + public function setNoteId(int $note_id): self { $this->note_id = $note_id; return $this; diff --git a/plugins/PinnedNotes/PinnedNotes.php b/plugins/PinnedNotes/PinnedNotes.php index 0e2e7d0e8c..b0f26bcdef 100644 --- a/plugins/PinnedNotes/PinnedNotes.php +++ b/plugins/PinnedNotes/PinnedNotes.php @@ -74,7 +74,7 @@ class PinnedNotes extends Plugin return Event::next; } - public function onBeforeFeed(Request $request, &$res): EventResult + public function onBeforeFeed(Request $request, array &$res): EventResult { $path = $request->attributes->get('_route'); if ($path === 'actor_view_nickname') { @@ -160,7 +160,7 @@ class PinnedNotes extends Plugin return Event::next; } - public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult + public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult { if ($type_name === 'Person') { $actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->get('id')); diff --git a/plugins/UnboundGroup/UnboundGroup.php b/plugins/UnboundGroup/UnboundGroup.php index 5f8c66f2f4..8a6affd022 100644 --- a/plugins/UnboundGroup/UnboundGroup.php +++ b/plugins/UnboundGroup/UnboundGroup.php @@ -83,7 +83,7 @@ class UnboundGroup extends Plugin return Event::next; } - public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult + public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult { if ($type_name === 'Group' || $type_name === 'Organization') { $actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->getId()); diff --git a/plugins/WebMonetization/WebMonetization.php b/plugins/WebMonetization/WebMonetization.php index 05338510da..f142a2ca52 100644 --- a/plugins/WebMonetization/WebMonetization.php +++ b/plugins/WebMonetization/WebMonetization.php @@ -55,7 +55,7 @@ class WebMonetization extends Plugin { /** * @param array $vars - * @param string[] $res + * @param string[] $res */ public function onAppendRightPanelBlock(Request $request, array $vars, array &$res): EventResult { @@ -63,9 +63,6 @@ class WebMonetization extends Plugin if (\is_null($user)) { return Event::next; } - if (\is_null($vars)) { - return Event::next; - } $is_self = null; $receiver_id = null; @@ -214,7 +211,7 @@ class WebMonetization extends Plugin ]; } - public function onAppendToHead(Request $request, &$res): EventResult + public function onAppendToHead(Request $request, array &$res): EventResult { $user = Common::user(); if (\is_null($user)) { @@ -257,7 +254,7 @@ class WebMonetization extends Plugin return Event::next; } - public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult + public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult { if ($type_name === 'Person') { $actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->getId()); diff --git a/plugins/XMPPNotifications/XMPPNotifications.php b/plugins/XMPPNotifications/XMPPNotifications.php index 475dd41441..36d0f787a6 100644 --- a/plugins/XMPPNotifications/XMPPNotifications.php +++ b/plugins/XMPPNotifications/XMPPNotifications.php @@ -40,7 +40,7 @@ use EventResult; class XMPPNotifications extends Plugin { - public function onAddNotificationTransport(&$form_defs): EventResult + public function onAddNotificationTransport(array &$form_defs): EventResult { $form_defs['XMPP'] = $form_defs['placeholder']; $form_defs['XMPP'][] = $form_defs['placeholder']['save']('XMPP', 'save_xmpp'); diff --git a/src/Core/Cache.php b/src/Core/Cache.php index 19b71cf0d5..7f6c5d45c5 100644 --- a/src/Core/Cache.php +++ b/src/Core/Cache.php @@ -40,8 +40,12 @@ use Symfony\Component\Cache\CacheItem; abstract class Cache { - protected static $pools; - protected static $redis; + protected static array $pools; + + /** + * @property ?Redis $redis + */ + protected static mixed $redis; /** * Configure a cache pool, with adapters taken from `ENV_VAR`. @@ -77,8 +81,10 @@ abstract class Cache // @codeCoverageIgnoreStart // This requires extra server configuration, but the code was tested // manually and works, so it'll be excluded from automatic tests, for now, at least - if (F\Every($dsns, function ($str) { [$scheme, $rest] = explode('://', $str); - return str_contains($rest, ':'); }) == false) { + if (F\Every($dsns, function ($str) { + [$scheme, $rest] = explode('://', $str); + return str_contains($rest, ':'); + }) == false) { throw new ConfigurationException('The configuration of a redis cluster requires specifying the ports to use'); } $class = RedisCluster::class; // true for persistent connection diff --git a/src/Core/Controller.php b/src/Core/Controller.php index 2b7f36b87c..0e19a5d351 100644 --- a/src/Core/Controller.php +++ b/src/Core/Controller.php @@ -153,7 +153,8 @@ abstract class Controller extends AbstractController implements EventSubscriberI $controller = $controller[0]; } if (is_subclass_of($controller, FeedController::class)) { - $this->vars = $controller->postProcess($this->vars); + // XXX + $this->vars = $controller->postProcess($this->vars); // @phpstan-ignore-line } // Respond in the most preferred acceptable content type @@ -162,10 +163,10 @@ abstract class Controller extends AbstractController implements EventSubscriberI $format = $request->getFormat($accept[0]); $potential_response = null; if (Event::handle('ControllerResponseInFormat', [ - 'route' => $route, + 'route' => $route, 'accept_header' => $accept, - 'vars' => $this->vars, - 'response' => &$potential_response, + 'vars' => $this->vars, + 'response' => &$potential_response, ]) !== Event::stop) { if ($redirect !== false) { $event->setResponse(new RedirectResponse($redirect)); @@ -274,7 +275,7 @@ abstract class Controller extends AbstractController implements EventSubscriberI // @codeCoverageIgnoreStart Log::critical($m = "Method '{$method}' on class App\\Core\\Controller not found (__call)"); throw new RuntimeException($m); - // @codeCoverageIgnoreEnd + // @codeCoverageIgnoreEnd } } } diff --git a/src/Core/DB.php b/src/Core/DB.php index ff611efabc..366d2ab409 100644 --- a/src/Core/DB.php +++ b/src/Core/DB.php @@ -45,6 +45,7 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Query; use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\QueryBuilder; @@ -89,8 +90,8 @@ use Functional as F; */ class DB { - private static ?EntityManagerInterface $em; - public static function setManager($m): void + private static null|\Doctrine\ORM\EntityManagerInterface|\Doctrine\Persistence\ObjectManager $em; + public static function setManager(EntityManagerInterface|\Doctrine\Persistence\ObjectManager $m): void { self::$em = $m; } @@ -104,6 +105,7 @@ class DB private static ?array $dql_table_name_patterns = null; public static function initTableMap() { + /** @var ClassMetadataInfo[] $all */ // @phpstan-ignore-next-line $all = self::$em->getMetadataFactory()->getAllMetadata(); foreach ($all as $meta) { self::$table_map[$meta->getTableName()] = $meta->getMetadataValue('name'); @@ -130,7 +132,7 @@ class DB public static function dql(string $query, array $params = [], array $options = []) { $query = preg_replace(self::$dql_table_name_patterns, self::$table_map, $query); - $q = new Query(self::$em); + $q = new Query(self::$em); // @phpstan-ignore-line $q->setDQL($query); if (isset($options['limit'])) { @@ -192,6 +194,7 @@ class DB if ($_ENV['APP_ENV'] === 'dev' && str_starts_with($query, 'select *')) { throw new Exception('Cannot use `select *`, use `select {select}` (see ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT)'); } + // @phpstan-ignore-next-line $rsmb = new ResultSetMappingBuilder(self::$em, \is_null($entities) ? ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT : ResultSetMappingBuilder::COLUMN_RENAMING_NONE); if (\is_null($entities)) { $matches = []; @@ -205,7 +208,8 @@ class DB $rsmb->addRootEntityFromClassMetadata($entity, $alias); } $query = preg_replace('/{select}/', $rsmb->generateSelectClause(), $query); - $q = self::$em->createNativeQuery($query, $rsmb); + // @phpstan-ignore-next-line + $q = self::$em->createNativeQuery($query, $rsmb); foreach ($params as $k => $v) { $q->setParameter($k, $v); } @@ -263,7 +267,7 @@ class DB { $criteria = array_change_key_case($criteria, \CASE_LOWER); $ops = array_intersect(array_keys($criteria), self::$find_by_ops); - /** @var EntityRepository */ + /** @var EntityRepository */ // @phpstan-ignore-next-line $repo = self::getRepository($table); if (empty($ops)) { return $repo->findBy($criteria, $order_by, $limit, $offset); @@ -307,7 +311,7 @@ class DB public static function count(string $table, array $criteria) { - /** @var EntityRepository */ + /** @var EntityRepository */ // @phpstan-ignore-next-line $repo = self::getRepository($table); return $repo->count($criteria); } diff --git a/src/Core/Form.php b/src/Core/Form.php index 3517dd1be6..a159419867 100644 --- a/src/Core/Form.php +++ b/src/Core/Form.php @@ -32,9 +32,7 @@ declare(strict_types = 1); namespace App\Core; -use App\Core\DB; use function App\Core\I18n\_m; -use App\Core\Router; use App\Util\Common; use App\Util\Exception\ClientException; use App\Util\Exception\ServerException; @@ -82,7 +80,7 @@ use Symfony\Component\HttpFoundation\Request; abstract class Form { private static ?FormFactoryInterface $form_factory; - public static function setFactory($ff): void + public static function setFactory(FormFactoryInterface $ff): void { self::$form_factory = $ff; } diff --git a/src/Core/GSFile.php b/src/Core/GSFile.php index 29bb21d5cd..2438241f49 100644 --- a/src/Core/GSFile.php +++ b/src/Core/GSFile.php @@ -204,7 +204,7 @@ class GSFile * Throw a client exception if the cache key $id doesn't contain * exactly one entry */ - public static function error($exception, $id, array $res) + public static function error(string $exception, int $id, array $res): mixed { switch (\count($res)) { case 0: diff --git a/src/Core/I18n/I18n.php b/src/Core/I18n/I18n.php index 78742616d9..022686c74f 100644 --- a/src/Core/I18n/I18n.php +++ b/src/Core/I18n/I18n.php @@ -66,7 +66,7 @@ abstract class I18n { public static ?TranslatorInterface $translator = null; - public static function setTranslator($trans): void + public static function setTranslator(TranslatorInterface $trans): void { self::$translator = $trans; } @@ -306,9 +306,11 @@ abstract class I18n * _m(string|string[] $msg, array $params) -- parameterized message * _m(string $ctx, string|string[] $msg, array $params) -- combination of the previous two * - * @throws ServerException - * * @todo add parameters + * + * @param array|int|string ...$args + * + * @throws ServerException */ function _m(...$args): string { @@ -317,18 +319,18 @@ function _m(...$args): string // and only 2 frames (this and previous) $domain = I18n::_mdomain(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2)[0]['file']); switch (\count($args)) { - case 1: - // Empty parameters, simple message - return I18n::$translator->trans($args[0], [], $domain, Common::currentLanguage()->getLocale()); - case 3: - // @codeCoverageIgnoreStart - if (\is_int($args[2])) { - throw new InvalidArgumentException('Calling `_m()` with a number for pluralization is deprecated, ' - . 'use an explicit parameter', ); - } - // @codeCoverageIgnoreEnd - // Falthrough - // no break + case 1: + // Empty parameters, simple message + return I18n::$translator->trans($args[0], [], $domain, Common::currentLanguage()->getLocale()); + case 3: + // @codeCoverageIgnoreStart + if (\is_int($args[2])) { + throw new InvalidArgumentException('Calling `_m()` with a number for pluralization is deprecated, ' + . 'use an explicit parameter', ); + } + // @codeCoverageIgnoreEnd + // Falthrough + // no break case 2: if (\is_array($args[0])) { $args[0] = I18n::formatICU($args[0], $args[1]); @@ -339,12 +341,12 @@ function _m(...$args): string $params = $args[1] ?? []; return I18n::$translator->trans($msg, $params, $domain, Common::currentLanguage()->getLocale()); } - // Fallthrough - // no break - default: - // @codeCoverageIgnoreStart - throw new InvalidArgumentException("Bad parameters to `_m()` for domain {$domain}"); - // @codeCoverageIgnoreEnd + // Fallthrough + // no break + default: + // @codeCoverageIgnoreStart + throw new InvalidArgumentException("Bad parameters to `_m()` for domain {$domain}"); + // @codeCoverageIgnoreEnd } } diff --git a/src/Core/I18n/TransExtractor.php b/src/Core/I18n/TransExtractor.php index 71db8ba237..fcef6af779 100644 --- a/src/Core/I18n/TransExtractor.php +++ b/src/Core/I18n/TransExtractor.php @@ -127,6 +127,8 @@ class TransExtractor extends AbstractFileExtractor implements ExtractorInterface /** * Normalizes a token. + * + * @param string $token */ protected function normalizeToken($token): ?string { @@ -186,6 +188,8 @@ class TransExtractor extends AbstractFileExtractor implements ExtractorInterface /** * {@inheritDoc} + * + * @param string $directory */ protected function extractFromDirectory($directory): iterable { diff --git a/src/Core/Log.php b/src/Core/Log.php index 2a7f99eb40..edde537bc0 100644 --- a/src/Core/Log.php +++ b/src/Core/Log.php @@ -53,7 +53,7 @@ abstract class Log { private static ?LoggerInterface $logger; - public static function setLogger($l): void + public static function setLogger(LoggerInterface $l): void { self::$logger = $l; } @@ -62,6 +62,7 @@ abstract class Log * Log a critical error when a really unexpected exception occured. This indicates a bug in the software * * @throws ServerException + * * @codeCoverageIgnore */ public static function unexpected_exception(Exception $e) diff --git a/src/Core/ModuleManager.php b/src/Core/ModuleManager.php index d9832cda29..d5e141ac10 100644 --- a/src/Core/ModuleManager.php +++ b/src/Core/ModuleManager.php @@ -52,11 +52,11 @@ use Symfony\Component\DependencyInjection\Reference; class ModuleManager { - protected static $loader; + protected static \Composer\Autoload\ClassLoader $loader; /** * @codeCoverageIgnore */ - public static function setLoader($l) + public static function setLoader(\Composer\Autoload\ClassLoader $l) { self::$loader = $l; } @@ -161,7 +161,7 @@ class ModuleManager /** * Serialize this class, for dumping into the cache */ - public static function __set_state($state) + public static function __set_state(array $state): self { $obj = new self(); $obj->modules = $state['modules']; diff --git a/src/Core/Modules/Module.php b/src/Core/Modules/Module.php index b0d93bc1f3..bc2e02cf4c 100644 --- a/src/Core/Modules/Module.php +++ b/src/Core/Modules/Module.php @@ -54,7 +54,7 @@ abstract class Module /** * Serialize the class to store in the cache */ - public static function __set_state($state) + public static function __set_state(array $state) { $obj = new (static::class); foreach ($state as $k => $v) { diff --git a/src/Core/Queue.php b/src/Core/Queue.php index cfd3f907cc..659b88f59d 100644 --- a/src/Core/Queue.php +++ b/src/Core/Queue.php @@ -40,7 +40,7 @@ abstract class Queue { private static ?MessageBusInterface $message_bus; - public static function setMessageBus($mb): void + public static function setMessageBus(MessageBusInterface $mb): void { self::$message_bus = $mb; } @@ -50,7 +50,7 @@ abstract class Queue * * @codeCoverageIgnore */ - public static function enqueue($payload, string $queue, bool $priority = false, array $stamps = []) + public static function enqueue(mixed $payload, string $queue, bool $priority = false, array $stamps = []): void { if ($priority) { self::$message_bus->dispatch(new MessageHigh($payload, $queue), $stamps); diff --git a/src/Core/Router.php b/src/Core/Router.php index add720e4f8..8a803e627b 100644 --- a/src/Core/Router.php +++ b/src/Core/Router.php @@ -32,8 +32,6 @@ declare(strict_types = 1); namespace App\Core; -use App\Core\Event; -use App\Core\Log; use App\Util\Common; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -41,6 +39,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Router as SymfonyRouter; +use Symfony\Component\Routing\RouterInterface; /** * @mixin SymfonyRouter @@ -70,9 +69,9 @@ class Router extends Loader */ public const NETWORK_PATH = UrlGeneratorInterface::NETWORK_PATH; - public static ?SymfonyRouter $router = null; + public static ?RouterInterface $router = null; - public static function setRouter($rtr): void + public static function setRouter(RouterInterface $rtr): void { self::$router = $rtr; } @@ -122,6 +121,7 @@ class Router extends Loader * * Must conform to symfony's interface, but the $resource is unused * and $type must not be null + * @param string $resource */ public function load($resource, ?string $type = null): RouteCollection { @@ -193,11 +193,11 @@ class Router extends Loader defaults: array_merge( [ '_controller' => \is_array($target) ? $target : [$target, '__invoke'], - '_format' => $options['format'] ?? 'html', - '_fragment' => $options['fragment'] ?? '', - '_locale' => $options['locale'] ?? 'en', - 'template' => $options['template'] ?? '', - 'accept' => $options['accept'] ?? [], + '_format' => $options['format'] ?? 'html', + '_fragment' => $options['fragment'] ?? '', + '_locale' => $options['locale'] ?? 'en', + 'template' => $options['template'] ?? '', + 'accept' => $options['accept'] ?? [], 'is_system_path' => $options['is_system_path'] ?? true, ], $options['defaults'] ?? [], diff --git a/src/Core/Router/RouteLoader.php b/src/Core/Router/RouteLoader.php index 584356df4c..5ed2619f09 100644 --- a/src/Core/Router/RouteLoader.php +++ b/src/Core/Router/RouteLoader.php @@ -50,6 +50,8 @@ class RouteLoader extends Loader * * Must conform to symfony's interface, but the $resource is unused * and $type must not be null + * + * @param resource $resource */ public function load($resource, ?string $type = null): RouteCollection { @@ -121,11 +123,11 @@ class RouteLoader extends Loader defaults: array_merge( [ '_controller' => \is_array($target) ? $target : [$target, '__invoke'], - '_format' => $options['format'] ?? 'html', - '_fragment' => $options['fragment'] ?? '', - '_locale' => $options['locale'] ?? 'en', - 'template' => $options['template'] ?? '', - 'accept' => $options['accept'] ?? [], + '_format' => $options['format'] ?? 'html', + '_fragment' => $options['fragment'] ?? '', + '_locale' => $options['locale'] ?? 'en', + 'template' => $options['template'] ?? '', + 'accept' => $options['accept'] ?? [], 'is_system_path' => $options['is_system_path'] ?? true, ], $options['defaults'] ?? [], diff --git a/src/Core/Security.php b/src/Core/Security.php index 9a57613c0a..e9f2d3eb82 100644 --- a/src/Core/Security.php +++ b/src/Core/Security.php @@ -46,6 +46,7 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent; * HtmlSanitizer\SanitizerInterface, calling the first existing method, in that order * * @codeCoverageIgnore + * * @mixin SymfonySecurity * * @method static LocalUser getUser() @@ -53,7 +54,7 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent; class Security implements EventSubscriberInterface //implements AuthenticatorInterface { private static ?SymfonySecurity $security; - public static function setHelper($sec): void + public static function setHelper(SymfonySecurity $sec): void { self::$security = $sec; } diff --git a/src/DependencyInjection/Compiler/SchemaDefDriver.php b/src/DependencyInjection/Compiler/SchemaDefDriver.php index ad4d41f9b3..b7c4d0c8d9 100644 --- a/src/DependencyInjection/Compiler/SchemaDefDriver.php +++ b/src/DependencyInjection/Compiler/SchemaDefDriver.php @@ -93,8 +93,11 @@ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface /** * Fill in the database $metadata for $class_name * - * @param class-string $class_name - * @param ClassMetadataInfo $metadata ClassMetadataInfo is the real type, but we need to override the method + * @param class-string $class_name + * + * @phpstan-ignore-next-line + * + * @param ClassMetadataInfo $metadata ClassMetadataInfo is the real type, but we need to override the method */ public function loadMetadataForClass($class_name, ClassMetadata $metadata): void { @@ -143,23 +146,23 @@ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface ]; switch ($opts['multiplicity']) { - case 'one to one': - $metadata->mapOneToOne($map); - break; - case 'many to one': - $metadata->mapManyToOne($map); - break; - case 'one to many': - $map['mappedBy'] = $target_field; - $metadata->mapOneToMany($map); - break; - case 'many to many': - $metadata->mapManyToMany($map); - break; - default: - throw new Exception("Invalid multiplicity specified: '${opts['multiplicity']}' in class: {$class_name}"); + case 'one to one': + $metadata->mapOneToOne($map); + break; + case 'many to one': + $metadata->mapManyToOne($map); + break; + case 'one to many': + $map['mappedBy'] = $target_field; + $metadata->mapOneToMany($map); + break; + case 'many to many': + $metadata->mapManyToMany($map); + break; + default: + throw new Exception("Invalid multiplicity specified: '${opts['multiplicity']}' in class: {$class_name}"); } - // @codeCoverageIgnoreEnd + // @codeCoverageIgnoreEnd } else { // Convert old to new types // For ints, prepend the size (smallint) @@ -213,6 +216,8 @@ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface * Override StaticPHPDriver's method, * we care about classes that have the method `schemaDef`, * instead of `loadMetadata`. + * + * @param mixed $class_name */ public function isTransient($class_name): bool { diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index 69a4c3f120..cd817d076a 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -550,6 +550,10 @@ class Actor extends Entity } } + public function hasBlocked(self $other): bool { + return false; + } + public function __call(string $name, array $arguments): mixed { if (Formatting::startsWith($name, 'is')) { diff --git a/src/Repository/ResetPasswordRequestRepository.php b/src/Repository/ResetPasswordRequestRepository.php index c55daa0a0d..ecb7efc540 100644 --- a/src/Repository/ResetPasswordRequestRepository.php +++ b/src/Repository/ResetPasswordRequestRepository.php @@ -17,6 +17,8 @@ use SymfonyCasts\Bundle\ResetPassword\Persistence\ResetPasswordRequestRepository * @method null|ResetPasswordRequest findOneBy(array $criteria, array $orderBy = null) * @method ResetPasswordRequest[] findAll() * @method ResetPasswordRequest[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + * + * @extends ServiceEntityRepository */ class ResetPasswordRequestRepository extends ServiceEntityRepository implements ResetPasswordRequestRepositoryInterface { diff --git a/src/Twig/Runtime.php b/src/Twig/Runtime.php index 6b1e00d9d1..15a367a9e4 100644 --- a/src/Twig/Runtime.php +++ b/src/Twig/Runtime.php @@ -116,6 +116,8 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface /** * @codeCoverageIgnore + * + * @param null|string ...$args */ public function getConfig(...$args) { @@ -125,10 +127,11 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface /** * get stylesheets * - * @return array|mixed * @codeCoverageIgnore + * + * @return array */ - public function getShowStylesheets($route) + public function getShowStylesheets(string $route): array { $styles = []; Event::handle('EndShowStyles', [&$styles, $route]); @@ -137,8 +140,11 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface /** * @codeCoverageIgnore + * + * @param mixed ...$args + * @return mixed[] */ - public function handleEvent(string $event, ...$args) + public function handleEvent(string $event, ...$args): array { $res = []; $args[] = &$res; @@ -164,7 +170,7 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface && (preg_match(pattern: $re_has_gecko, subject: $this->request->headers->get('User-Agent')) === 1); } - public function isInstanceOf($value, string $type): bool + public function isInstanceOf(mixed $value, string $type): bool { return (\function_exists($func = 'is_' . $type) && $func($value)) || $value instanceof $type; } diff --git a/src/Util/Common.php b/src/Util/Common.php index 662e5a39ab..eeb8de3790 100644 --- a/src/Util/Common.php +++ b/src/Util/Common.php @@ -108,7 +108,7 @@ abstract class Common * * @param bool $transient keep this setting in memory only */ - public static function setConfig(string $section, string $setting, $value, bool $transient = false): void + public static function setConfig(string $section, string $setting, mixed $value, bool $transient = false): void { self::$config[$section][$setting] = $value; if (!$transient) { @@ -184,8 +184,13 @@ abstract class Common /** * A recursive `array_diff`, while PHP itself doesn't provide one + * + * @param mixed[] $array1 + * @param mixed[] $array2 + * + * @return mixed[] */ - public static function arrayDiffRecursive($array1, $array2): array + public static function arrayDiffRecursive(array $array1, array $array2): array { $diff = []; foreach ($array1 as $key => $value) { diff --git a/src/Util/Formatting.php b/src/Util/Formatting.php index 5995922b7c..6c6836243e 100644 --- a/src/Util/Formatting.php +++ b/src/Util/Formatting.php @@ -191,7 +191,7 @@ abstract class Formatting /** * Convert scalars, objects implementing __toString or arrays to strings */ - public static function toString($value, string $join_type = self::JOIN_BY_COMMA): string + public static function toString(mixed $value, string $join_type = self::JOIN_BY_COMMA): string { if (!\in_array($join_type, [static::JOIN_BY_SPACE, static::JOIN_BY_COMMA])) { throw new Exception('Formatting::toString received invalid join option'); @@ -209,7 +209,7 @@ abstract class Formatting * * @param static::SPLIT_BY_BOTH|static::SPLIT_BY_COMMA|static::SPLIT_BY_SPACE $split_type */ - public static function toArray(string $input, &$output, string $split_type = self::SPLIT_BY_COMMA): bool + public static function toArray(string $input, array &$output, string $split_type = self::SPLIT_BY_COMMA): bool { if ($input == '') { $output = []; diff --git a/src/Util/HTML.php b/src/Util/HTML.php index 2c5f81fc70..efb7dcf280 100644 --- a/src/Util/HTML.php +++ b/src/Util/HTML.php @@ -57,7 +57,7 @@ abstract class HTML public const SELF_CLOSING_TAG = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'link', 'meta', 'param', 'source', 'track', 'wbr']; private static ?SanitizerInterface $sanitizer; - public static function setSanitizer($sanitizer): void + public static function setSanitizer(SanitizerInterface $sanitizer): void { self::$sanitizer = $sanitizer; } diff --git a/src/Util/TemporaryFile.php b/src/Util/TemporaryFile.php index 6e7f1bb8c4..ea4152187c 100644 --- a/src/Util/TemporaryFile.php +++ b/src/Util/TemporaryFile.php @@ -42,7 +42,9 @@ use Symfony\Component\Mime\MimeTypes; */ class TemporaryFile extends SplFileInfo { - // Cannot type annotate currently. `resource` is the expected type, but it's not a builtin type + /** + * @var null|false|resource $resource + */ protected $resource; /**