From edeee49af997db9bdf3c3150eb0c00768cf4209d Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Wed, 19 Oct 2022 22:38:49 +0100 Subject: [PATCH] [TOOLS] Fix errors pointed out by PHPStan level 4 --- components/Conversation/Conversation.php | 14 ------ components/LeftPanel/Controller/EditFeeds.php | 1 - components/Link/Link.php | 6 +-- components/Posting/Posting.php | 5 +- components/Search/Search.php | 2 - components/Subscription/Subscription.php | 4 +- components/Tag/Tag.php | 4 +- .../AttachmentShowRelated.php | 2 - .../Bundles/Controller/BundleCollections.php | 2 +- plugins/Cover/Cover.php | 4 -- plugins/DeleteNote/DeleteNote.php | 14 +----- plugins/Directory/Directory.php | 6 --- plugins/Embed/Embed.php | 2 - plugins/Favourite/Favourite.php | 14 +----- plugins/FileQuota/FileQuota.php | 3 +- plugins/ImageEncoder/ImageEncoder.php | 2 - .../NoteTypeFeedFilter/NoteTypeFeedFilter.php | 30 ++++++----- plugins/Pinboard/Controller/APIv1.php | 15 +++--- plugins/Pinboard/Controller/Settings.php | 8 +-- plugins/Pinboard/Entity/Token.php | 2 + plugins/ProfileColor/ProfileColor.php | 2 - plugins/RepeatNote/RepeatNote.php | 28 ++--------- plugins/StoreRemoteMedia/StoreRemoteMedia.php | 2 - plugins/WebHooks/WebHooks.php | 50 ++++++++++--------- src/Core/DB.php | 29 ++++++----- src/Core/Entity.php | 2 + src/Core/Event.php | 14 +++--- src/Core/GSFile.php | 6 +-- src/Core/ModuleManager.php | 3 +- src/Core/Modules/NoteHandlerPlugin.php | 2 - src/Entity/Actor.php | 1 + src/Entity/LocalUser.php | 4 ++ src/Util/Common.php | 42 ++++++++-------- .../Exception/InvalidRequestException.php | 34 +++++++++++++ src/Util/Form/ActorForms.php | 2 +- src/Util/Formatting.php | 3 +- tests/Core/EntityTest.php | 2 +- 37 files changed, 168 insertions(+), 198 deletions(-) create mode 100644 src/Util/Exception/InvalidRequestException.php diff --git a/components/Conversation/Conversation.php b/components/Conversation/Conversation.php index ac6c9ac9e5..96bb283b7d 100644 --- a/components/Conversation/Conversation.php +++ b/components/Conversation/Conversation.php @@ -102,8 +102,6 @@ class Conversation extends Component * 'id' (HTML markup id used to redirect user to this anchor upon performing the action) * * @throws \App\Util\Exception\ServerException - * - * @return bool EventHook */ public function onAddNoteActions(Request $request, Note $note, array &$actions): EventResult { @@ -142,8 +140,6 @@ class Conversation extends Component * * @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']) - * - * @return bool EventHook */ public function onAppendCardNote(array $vars, array &$result): EventResult { @@ -195,8 +191,6 @@ class Conversation extends Component * * @param \App\Entity\Actor $actor The Actor currently attempting to post a Note * @param null|\App\Entity\Actor $context_actor The 'owner' of the current route (e.g. Group or Actor), used to target it - * - * @return bool EventHook */ public function onPostingGetContextActor(Request $request, Actor $actor, ?Actor &$context_actor): EventResult { @@ -216,8 +210,6 @@ class Conversation extends Component * * @throws \App\Util\Exception\ClientException * @throws \App\Util\Exception\NoSuchNoteException - * - * @return bool EventHook */ public function onPostingModifyData(Request $request, Actor $actor, array &$data): EventResult { @@ -245,8 +237,6 @@ class Conversation extends Component * * @param \App\Entity\Note $note Note being deleted * @param \App\Entity\Actor $actor Actor that performed the delete action - * - * @return bool EventHook */ public function onNoteDeleteRelated(Note &$note, Actor $actor): EventResult { @@ -264,8 +254,6 @@ class Conversation extends Component * @param array $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 - * - * @return bool EventHook */ public function onAddExtraNoteActions(Request $request, Note $note, array &$actions): EventResult { @@ -300,8 +288,6 @@ class Conversation extends Component * Prevents new Notifications to appear for muted conversations * * @param Activity $activity Notification Activity - * - * @return bool EventHook */ public function onNewNotificationShould(Activity $activity, Actor $actor): EventResult { diff --git a/components/LeftPanel/Controller/EditFeeds.php b/components/LeftPanel/Controller/EditFeeds.php index f6e9d7bdb7..cb75271a43 100644 --- a/components/LeftPanel/Controller/EditFeeds.php +++ b/components/LeftPanel/Controller/EditFeeds.php @@ -119,7 +119,6 @@ class EditFeeds extends Controller /** @var SubmitButton $remove_button */ $remove_button = $form->get($remove_id); if ($remove_button->isClicked()) { - // @phpstan-ignore-next-line -- Doesn't quite understand that _this_ $opts for the current $form_definitions does have 'data' DB::remove(DB::getReference('feed', ['actor_id' => $user->getId(), 'url' => $opts['data']])); DB::flush(); Cache::delete($key); diff --git a/components/Link/Link.php b/components/Link/Link.php index 2c01851875..5afc61be0b 100644 --- a/components/Link/Link.php +++ b/components/Link/Link.php @@ -39,12 +39,12 @@ class Link extends Component /** * Note that this persists both a Link and a NoteToLink * - * @return [Entity\Link, NoteToLink] + * @return array{ link: ?Entity\Link, note_to_link: ?NoteToLink } */ public static function maybeCreateLink(string $url, int $note_id): array { try { - $link = Entity\Link::getOrCreate($url); + $link = Entity\Link::getOrCreate($url); DB::persist($note_link = NoteToLink::create(['link_id' => $link->getId(), 'note_id' => $note_id])); return ['link' => $link, 'note_to_link' => $note_link]; } catch (InvalidArgumentException) { @@ -66,7 +66,7 @@ class Link extends Component if (\in_array($match, $ignore)) { continue; } - self::maybeCreateLink($match, $note_id); + self::maybeCreateLink($match, $note->getId()); } } return Event::next; diff --git a/components/Posting/Posting.php b/components/Posting/Posting.php index 2193e816ef..cac1e5e5f0 100644 --- a/components/Posting/Posting.php +++ b/components/Posting/Posting.php @@ -33,6 +33,7 @@ use App\Core\Router; use App\Core\VisibilityScope; use App\Entity\Activity; use App\Entity\Actor; +use App\Entity\LocalUser; use App\Entity\Note; use App\Util\Common; use App\Util\Exception\BugFoundException; @@ -180,7 +181,9 @@ class Posting extends Component ): array { $scope ??= VisibilityScope::EVERYWHERE; // TODO: If site is private, default to LOCAL $reply_to_id = \is_null($reply_to) ? null : (\is_int($reply_to) ? $reply_to : $reply_to->getId()); - $mentions = []; + + /** @var array }> $mentions */ + $mentions = []; if (\is_null($rendered) && !empty($content)) { Event::handle('RenderNoteContent', [$content, $content_type, &$rendered, $actor, $locale, &$mentions]); } diff --git a/components/Search/Search.php b/components/Search/Search.php index ad8c836e64..279f652af4 100644 --- a/components/Search/Search.php +++ b/components/Search/Search.php @@ -146,8 +146,6 @@ class Search extends Component * Output our dedicated stylesheet * * @param array $styles stylesheets path - * - * @return bool hook value; true means continue processing, false means stop */ public function onEndShowStyles(array &$styles, string $route): EventResult { diff --git a/components/Subscription/Subscription.php b/components/Subscription/Subscription.php index 8781427a5c..09ec67f907 100644 --- a/components/Subscription/Subscription.php +++ b/components/Subscription/Subscription.php @@ -98,7 +98,7 @@ class Subscription extends Component $activity = null; if (\is_null($subscription)) { DB::persist($subscription = Entity\ActorSubscription::create($opts)); - $activity = Activity::create([ + $activity = Activity::create([ 'actor_id' => $subscriber_id, 'verb' => 'subscribe', 'object_type' => Actor::schemaName(), @@ -184,8 +184,6 @@ class Subscription extends Component * @throws DuplicateFoundException * @throws NotFoundException * @throws ServerException - * - * @return bool EventHook */ public function onAddProfileActions(Request $request, Actor $object, array &$actions): EventResult { diff --git a/components/Tag/Tag.php b/components/Tag/Tag.php index 1edb313650..a0f97ec7ea 100644 --- a/components/Tag/Tag.php +++ b/components/Tag/Tag.php @@ -68,12 +68,12 @@ class Tag extends Component return Event::next; } - public static function maybeCreateTag(string $tag, int $note_id, ?int $lang_id): ?NoteTag + public static function maybeCreateTag(string $tag, int $note_id, ?int $lang_id, array $extra_args = []): ?NoteTag { if (!self::validate($tag)) { return null; // Ignore invalid tag candidates } - $canonical_tag = self::canonicalTag($tag, \is_null($lang_id) ? null : Language::getById($lang_id)->getLocale()); + $canonical_tag = self::canonicalTag($tag, \is_null($lang_id) ? null : Language::getById($lang_id)->getLocale()); DB::persist($note_tag = NoteTag::create([ 'tag' => $tag, 'canonical' => $canonical_tag, diff --git a/plugins/AttachmentShowRelated/AttachmentShowRelated.php b/plugins/AttachmentShowRelated/AttachmentShowRelated.php index c99d352160..0df6dea45d 100644 --- a/plugins/AttachmentShowRelated/AttachmentShowRelated.php +++ b/plugins/AttachmentShowRelated/AttachmentShowRelated.php @@ -52,8 +52,6 @@ class AttachmentShowRelated extends Plugin * Output our dedicated stylesheet * * @param array $styles stylesheets path - * - * @return bool hook value; true means continue processing, false means stop */ public function onEndShowStyles(array &$styles, string $path): EventResult { diff --git a/plugins/Bundles/Controller/BundleCollections.php b/plugins/Bundles/Controller/BundleCollections.php index 6417676eeb..d47c0d6d49 100644 --- a/plugins/Bundles/Controller/BundleCollections.php +++ b/plugins/Bundles/Controller/BundleCollections.php @@ -30,7 +30,7 @@ use Plugin\Bundles\Entity\BundleCollection; class BundleCollections extends MetaCollectionController { - public function getCollectionUrl(int $owner_id, string $owner_nickname, int $collection_id): string + public function getCollectionUrl(int $owner_id, ?string $owner_nickname, int $collection_id): string { if (\is_null($owner_nickname)) { return Router::url( diff --git a/plugins/Cover/Cover.php b/plugins/Cover/Cover.php index 1bc692a07e..562d82d523 100644 --- a/plugins/Cover/Cover.php +++ b/plugins/Cover/Cover.php @@ -45,8 +45,6 @@ class Cover extends Plugin { /** * Map URLs to actions - * - * @return bool hook value; true means continue processing, false means stop */ public function onAddRoute(Router $r): EventResult { @@ -90,8 +88,6 @@ class Cover extends Plugin * Output our dedicated stylesheet * * @param array $styles stylesheets path - * - * @return bool hook value; true means continue processing, false means stop */ public function onStartShowStyles(array &$styles): EventResult { diff --git a/plugins/DeleteNote/DeleteNote.php b/plugins/DeleteNote/DeleteNote.php index a925ebd2da..2139d2ff55 100644 --- a/plugins/DeleteNote/DeleteNote.php +++ b/plugins/DeleteNote/DeleteNote.php @@ -133,8 +133,6 @@ class DeleteNote extends NoteHandlerPlugin /** * Adds and connects the _delete_note_action_ route to * Controller\DeleteNote::class - * - * @return bool Event hook */ public function onAddRoute(Router $r): EventResult { @@ -152,8 +150,6 @@ class DeleteNote extends NoteHandlerPlugin * @throws \App\Util\Exception\DuplicateFoundException * @throws \App\Util\Exception\NotFoundException * @throws \App\Util\Exception\ServerException - * - * @return bool Event hook */ public function onAddExtraNoteActions(Request $request, Note $note, array &$actions): EventResult { @@ -193,10 +189,8 @@ class DeleteNote extends NoteHandlerPlugin * @param \ActivityPhp\Type\AbstractObject $type_activity Activity Streams 2.0 Activity * @param mixed $type_object Activity's Object * @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ - private function activitypub_handler(Actor $actor, AbstractObject $type_activity, mixed $type_object, ?ActivitypubActivity &$ap_act): bool + private function activitypub_handler(Actor $actor, AbstractObject $type_activity, mixed $type_object, ?ActivitypubActivity &$ap_act): EventResult { if ($type_activity->get('type') !== 'Delete' || !($type_object instanceof Note)) { @@ -225,8 +219,6 @@ class DeleteNote extends NoteHandlerPlugin * @param \ActivityPhp\Type\AbstractObject $type_activity Activity Streams 2.0 Activity * @param \ActivityPhp\Type\AbstractObject $type_object Activity Streams 2.0 Object * @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivity(Actor $actor, AbstractObject $type_activity, AbstractObject $type_object, ?ActivitypubActivity &$ap_act): EventResult { @@ -240,8 +232,6 @@ class DeleteNote extends NoteHandlerPlugin * @param \ActivityPhp\Type\AbstractObject $type_activity Activity Streams 2.0 Activity * @param mixed $type_object Object * @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivityWithObject(Actor $actor, AbstractObject $type_activity, mixed $type_object, ?ActivitypubActivity &$ap_act): EventResult { @@ -253,8 +243,6 @@ class DeleteNote extends NoteHandlerPlugin * * @param string $verb GNU social's internal verb * @param null|string $gs_verb_to_activity_stream_two_verb Resulting Activity Streams 2.0 verb - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onGSVerbToActivityStreamsTwoActivityType(string $verb, ?string &$gs_verb_to_activity_stream_two_verb): EventResult { diff --git a/plugins/Directory/Directory.php b/plugins/Directory/Directory.php index b089b09d26..edb9a86c25 100644 --- a/plugins/Directory/Directory.php +++ b/plugins/Directory/Directory.php @@ -38,8 +38,6 @@ class Directory extends Plugin { /** * Map Directory routes to its corresponding Controllers - * - * @return bool */ public function onAddRoute(Router $r): EventResult { @@ -53,8 +51,6 @@ class Directory extends Plugin * Add Links to main navigation card * * @param array $res out menu items - * - * @return bool hook value; true means continue processing, false means stop */ public function onAddMainNavigationItem(array $vars, array &$res): EventResult { @@ -70,8 +66,6 @@ class Directory extends Plugin * * @throws RedirectException * @throws ServerException - * - * @return bool EventHook */ public function onPrependActorsCollection(Request $request, array &$elements): EventResult { diff --git a/plugins/Embed/Embed.php b/plugins/Embed/Embed.php index 3eff808b74..3d36fbeaef 100644 --- a/plugins/Embed/Embed.php +++ b/plugins/Embed/Embed.php @@ -387,8 +387,6 @@ class Embed extends Plugin * @param array $versions inherited from parent * * @throws ServerException - * - * @return bool true hook value */ public function onPluginVersion(array &$versions): EventResult { diff --git a/plugins/Favourite/Favourite.php b/plugins/Favourite/Favourite.php index d306d9fa23..5bb6955fff 100644 --- a/plugins/Favourite/Favourite.php +++ b/plugins/Favourite/Favourite.php @@ -123,8 +123,6 @@ class Favourite extends NoteHandlerPlugin * * @param Note $note Current Note being rendered * @param array $actions Array containing all Note actions to be rendered - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onAddNoteActions(Request $request, Note $note, array &$actions): EventResult { @@ -169,8 +167,6 @@ class Favourite extends NoteHandlerPlugin * * @param array $vars Array containing necessary info to process event. In this case, contains the current Note being rendered * @param array $result Contains a hashmap for each Activity performed on Note (object) - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onAppendCardNote(array $vars, array &$result): EventResult { @@ -271,10 +267,8 @@ class Favourite extends NoteHandlerPlugin * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ - private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool + private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { if (!\in_array($type_activity->get('type'), ['Like', 'Undo'])) { return Event::next; @@ -364,8 +358,6 @@ class Favourite extends NoteHandlerPlugin * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivity(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, \ActivityPhp\Type\AbstractObject $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { @@ -388,8 +380,6 @@ class Favourite extends NoteHandlerPlugin * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivityWithObject(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { @@ -401,8 +391,6 @@ class Favourite extends NoteHandlerPlugin * * @param string $verb GNU social's internal verb * @param null|string $gs_verb_to_activity_stream_two_verb Resulting Activity Streams 2.0 verb - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onGSVerbToActivityStreamsTwoActivityType(string $verb, ?string &$gs_verb_to_activity_stream_two_verb): EventResult { diff --git a/plugins/FileQuota/FileQuota.php b/plugins/FileQuota/FileQuota.php index ae54108c5b..b49e293ff4 100644 --- a/plugins/FileQuota/FileQuota.php +++ b/plugins/FileQuota/FileQuota.php @@ -37,6 +37,7 @@ use EventResult; * Check attachment file size quotas * * @package GNUsocial + * * @ccategory Attachment * * @author Hugo Sales @@ -102,8 +103,6 @@ class FileQuota extends Plugin * Adds this plugin's version information to $versions array * * @param array $versions inherited from parent - * - * @return bool true hook value */ public function onPluginVersion(array &$versions): EventResult { diff --git a/plugins/ImageEncoder/ImageEncoder.php b/plugins/ImageEncoder/ImageEncoder.php index 2ef0e3dda9..64ee7eec49 100644 --- a/plugins/ImageEncoder/ImageEncoder.php +++ b/plugins/ImageEncoder/ImageEncoder.php @@ -258,8 +258,6 @@ class ImageEncoder extends Plugin * Adds this plugin's version information to $versions array * * @param array $versions inherited from parent - * - * @return bool true hook value */ public function onPluginVersion(array &$versions): EventResult { diff --git a/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php b/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php index 4aa9d0fb5c..ba1d386161 100644 --- a/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php +++ b/plugins/NoteTypeFeedFilter/NoteTypeFeedFilter.php @@ -111,20 +111,20 @@ class NoteTypeFeedFilter extends Plugin $is_negate = $type[0] === '!'; $type = Formatting::removePrefix($type, '!'); switch ($type) { - case 'text': - $ret = !\is_null($note->getContent()); - break; - case 'media': - $ret = !empty($note->getAttachments()); - break; - case 'link': - $ret = !empty($note->getLinks()); - break; - case 'tag': - $ret = !empty($note->getTags()); - break; - default: - throw new BugFoundException("Unkown note type requested {$type}", previous: $this->unknownType($type)); + case 'text': + $ret = !\is_null($note->getContent()); + break; + case 'media': + $ret = !empty($note->getAttachments()); + break; + case 'link': + $ret = !empty($note->getLinks()); + break; + case 'tag': + $ret = !empty($note->getTags()); + break; + default: + throw new BugFoundException("Unkown note type requested {$type}", previous: $this->unknownType($type)); } if ($is_negate && $ret) { return false; @@ -182,8 +182,6 @@ class NoteTypeFeedFilter extends Plugin * Output our dedicated stylesheet * * @param array $styles stylesheets path - * - * @return bool hook value; true means continue processing, false means stop */ public function onEndShowStyles(array &$styles, string $route): EventResult { diff --git a/plugins/Pinboard/Controller/APIv1.php b/plugins/Pinboard/Controller/APIv1.php index bd909d494e..38bc9389fd 100644 --- a/plugins/Pinboard/Controller/APIv1.php +++ b/plugins/Pinboard/Controller/APIv1.php @@ -14,6 +14,7 @@ use App\Entity\LocalUser; use App\Entity\Note; use App\Util\Exception\BugFoundException; use App\Util\Exception\ClientException; +use App\Util\Exception\InvalidRequestException; use App\Util\Exception\ServerException; use App\Util\Formatting; use Component\Conversation\Conversation; @@ -68,11 +69,13 @@ class APIv1 extends Controller // $encoder = new XmlEncoder(); // $xml = $encoder->encode($result, 'xml'); [$tag_names, $keys] = F\zip(...F\map(array_keys($result), fn (string $k) => explode('_', $k))); - $xml = new SimpleXMLElement('<' . $tag_names[0] . '/>'); - dd($tag_names, $keys, $result, $xml, (string) $xml); - $xml->addChild(); - dd($xml); - return new Response(content: $xml, status: $statis); + $xml = new SimpleXMLElement('<' . $tag_names[0] . '/>'); + // dd($tag_names, $keys, $result, $xml, (string) $xml); + // $xml->addChild(); + // dd($xml); + return new Response(content: $xml, status: $status); + } else { + throw new InvalidRequestException; } } @@ -308,7 +311,7 @@ class APIv1 extends Controller } elseif (!\is_null($day)) { $day = new DateTimeImmutable($day); $pins = DB::findBy(Pin::class, ['actor_id' => $user->getId(), 'gte' => ['modified' => $day], 'lt' => ['modified' => $day->modify('+1 day')]]); - } elseif (!\is_null($url)) { + } elseif (!\is_null($url)) { // @phpstan-ignore-line this is here for clarity $pins = DB::findBy(Pin::class, ['actor_id' => $user->getId(), 'url_hash' => hash('sha256', $url)]); } else { throw new BugFoundException('Wonky logic in pinboard/posts/get'); diff --git a/plugins/Pinboard/Controller/Settings.php b/plugins/Pinboard/Controller/Settings.php index 6791c43145..8a78f95d6b 100644 --- a/plugins/Pinboard/Controller/Settings.php +++ b/plugins/Pinboard/Controller/Settings.php @@ -53,19 +53,21 @@ class Settings extends Controller $enable_button = $form->get('enable'); /** @var SubmitButton $regenerate_button */ $regenerate_button = $form->get('regenerate'); + /** @var Token $token */ + $token = $params['token']; if ($enable_button->isClicked()) { if ($params['was_enabled']) { if (\is_null($params['token'])) { throw new BugFoundException('Pinboard API can not be enabled if no token is present'); } else { - $token = DB::refetch($parms['token']); + DB::refresh($token); $token->setEnabled(false); } } else { if (\is_null($params['token'])) { DB::persist($token = Token::create(['actor_id' => $user->getId(), 'token' => Token::generateTokenString(), 'enabled' => true])); } else { - $token = DB::refetch($parms['token']); + DB::refresh($params['token']); $token->setEnabled(true); } } @@ -73,7 +75,7 @@ class Settings extends Controller if (\is_null($params['token'])) { throw new ClientException(_m('Can not regenerate token when no token exists. Enable Pinboard first')); } else { - $token = DB::refetch($params['token']); + DB::refresh($params['token']); $token->setToken(Token::generateTokenString()); } } else { diff --git a/plugins/Pinboard/Entity/Token.php b/plugins/Pinboard/Entity/Token.php index 701ef2a508..6949b9545c 100644 --- a/plugins/Pinboard/Entity/Token.php +++ b/plugins/Pinboard/Entity/Token.php @@ -107,6 +107,8 @@ class Token extends Entity options: ['limit' => 1], ), ); + } else { + return null; } } diff --git a/plugins/ProfileColor/ProfileColor.php b/plugins/ProfileColor/ProfileColor.php index 9c51e8e35b..c83f7b15f2 100644 --- a/plugins/ProfileColor/ProfileColor.php +++ b/plugins/ProfileColor/ProfileColor.php @@ -49,8 +49,6 @@ class ProfileColor extends Plugin { /** * Map URLs to actions - * - * @return bool hook value; true means continue processing, false means stop */ public function onAddRoute(Router $r): EventResult { diff --git a/plugins/RepeatNote/RepeatNote.php b/plugins/RepeatNote/RepeatNote.php index 9e71aab036..c214e97c14 100644 --- a/plugins/RepeatNote/RepeatNote.php +++ b/plugins/RepeatNote/RepeatNote.php @@ -81,7 +81,7 @@ class RepeatNote extends NoteHandlerPlugin $original_note_id = $note->getId(); // Create a new note with the same content as the original - [, $repeat, ] = Posting::storeLocalNote( + [, $repeat] = Posting::storeLocalNote( actor: $actor = Actor::getById($actor_id), content: $note->getContent(), content_type: $note->getContentType(), @@ -186,10 +186,10 @@ class RepeatNote extends NoteHandlerPlugin } else { // Either was undoed already if (!\is_null($already_repeated_activity = DB::findOneBy(Activity::class, [ - 'actor_id' => $actor_id, - 'verb' => 'repeat', + 'actor_id' => $actor_id, + 'verb' => 'repeat', 'object_type' => 'note', - 'object_id' => $note_id, + 'object_id' => $note_id, ], return_null: true))) { return DB::findOneBy(Activity::class, [ 'actor_id' => $actor_id, @@ -209,8 +209,6 @@ class RepeatNote extends NoteHandlerPlugin * original Note on Actor feed * * @param array $notes List of Notes to be filtered - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onFilterNoteList(?Actor $actor, array &$notes, Request $request): EventResult { @@ -234,8 +232,6 @@ class RepeatNote extends NoteHandlerPlugin /** * HTML rendering event that adds the repeat form as a note * action, if a user is logged in - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onAddNoteActions(Request $request, Note $note, array &$actions): EventResult { @@ -278,8 +274,6 @@ class RepeatNote extends NoteHandlerPlugin * @param array $vars Contains the Note currently being rendered * @param array $result Rendered String containing anchors for Actors that * repeated the Note - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onAppendCardNote(array $vars, array &$result): EventResult { @@ -316,8 +310,6 @@ class RepeatNote extends NoteHandlerPlugin * * @param Note $note Note to be deleted * @param Actor $actor Who performed the Delete action - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onNoteDeleteRelated(Note &$note, Actor $actor): EventResult { @@ -338,8 +330,6 @@ class RepeatNote extends NoteHandlerPlugin * * - **repeat_remove** * same as above, except that it undoes the aforementioned action - * - * @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers */ public function onAddRoute(Router $r): EventResult { @@ -369,10 +359,8 @@ class RepeatNote extends NoteHandlerPlugin * @throws ClientException * @throws DuplicateFoundException * @throws ServerException - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ - private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool + private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { if (!\in_array($type_activity->get('type'), ['Announce', 'Undo']) || !$actor->isPerson()) { return Event::next; @@ -463,8 +451,6 @@ class RepeatNote extends NoteHandlerPlugin * @throws ClientException * @throws DuplicateFoundException * @throws ServerException - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivity(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, \ActivityPhp\Type\AbstractObject $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { @@ -488,8 +474,6 @@ class RepeatNote extends NoteHandlerPlugin * @throws ClientException * @throws DuplicateFoundException * @throws ServerException - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onNewActivityPubActivityWithObject(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): EventResult { @@ -501,8 +485,6 @@ class RepeatNote extends NoteHandlerPlugin * * @param string $verb GNU social's internal verb * @param null|string $gs_verb_to_activity_stream_two_verb Resulting Activity Streams 2.0 verb - * - * @return bool Returns `Event::stop` if handled, `Event::next` otherwise */ public function onGSVerbToActivityStreamsTwoActivityType(string $verb, ?string &$gs_verb_to_activity_stream_two_verb): EventResult { diff --git a/plugins/StoreRemoteMedia/StoreRemoteMedia.php b/plugins/StoreRemoteMedia/StoreRemoteMedia.php index 5f0942ad54..a3b94a5e7d 100644 --- a/plugins/StoreRemoteMedia/StoreRemoteMedia.php +++ b/plugins/StoreRemoteMedia/StoreRemoteMedia.php @@ -234,8 +234,6 @@ class StoreRemoteMedia extends Plugin * Adds this plugin's version information to $versions array * * @param array $versions inherited from parent - * - * @return bool true hook value */ public function onPluginVersion(array &$versions): EventResult { diff --git a/plugins/WebHooks/WebHooks.php b/plugins/WebHooks/WebHooks.php index 2dcbef10cf..9fe2b75443 100644 --- a/plugins/WebHooks/WebHooks.php +++ b/plugins/WebHooks/WebHooks.php @@ -40,6 +40,10 @@ use Plugin\WebHooks\Controller as C; use Plugin\WebHooks\Entity as E; use Symfony\Component\HttpFoundation\Request; +/** + * @phpstan-type AliasNotifications array{ sender: Actor, activity: Activity, effective_targets: array, reason: ?string } + * @phpstan-type AliasSubscriptions array{ subscriber: Actor, activity: Activity, target: Actor, reason: ?string } + */ class WebHooks extends Plugin { public const controller_route = 'webhook'; @@ -86,33 +90,33 @@ class WebHooks extends Plugin } /** - * @param array $args + * @param AliasNotifications|AliasSubscriptions $args */ public function onQueueWebhook(string $type, string $hook_target, Actor $actor, array $args): EventResult { switch ($type) { - case 'notifications': - [$sender, $activity, $targets, $reason] = $args; - $data = [ - 'type' => 'notification', - 'activity' => '%activity%', - 'actor' => ['id' => $sender->getId(), 'nickname' => $sender->getNickname()], - 'targets' => F\map(array_values($targets), fn (Actor $actor) => ['id' => $actor->getId(), 'nickname' => $actor->getNickname()]), - 'reason' => $reason, - ]; - break; - case 'subscriptions': - [$subscriber, $activity, $target, $reason] = $args; - $data = [ - 'type' => 'subscription', - 'activity' => '%activity%', - 'actor' => ['id' => $subscriber->getId(), 'nickname' => $subscriber->getNickname()], - 'targets' => [['id' => $target->getId(), 'nickname' => $target->getNickname()]], - 'reason' => $reason, - ]; - break; - default: - throw new ServerException("Webhook notification handler for event {$type} not implemented"); + case 'notifications': + ['sender' => $sender, 'activity' => $activity, 'effective_targets' => $targets, 'reason' => $reason] = $args; + $data = [ + 'type' => 'notification', + 'activity' => '%activity%', + 'actor' => ['id' => $sender->getId(), 'nickname' => $sender->getNickname()], + 'targets' => F\map(array_values($targets), fn (Actor $actor) => ['id' => $actor->getId(), 'nickname' => $actor->getNickname()]), + 'reason' => $reason, + ]; + break; + case 'subscriptions': + ['subscriber' => $subscriber, 'activity' => $activity, 'target' => $target, 'reason' => $reason] = $args; + $data = [ + 'type' => 'subscription', + 'activity' => '%activity%', + 'actor' => ['id' => $subscriber->getId(), 'nickname' => $subscriber->getNickname()], + 'targets' => [['id' => $target->getId(), 'nickname' => $target->getNickname()]], + 'reason' => $reason, + ]; + break; + default: + throw new ServerException("Webhook notification handler for event {$type} not implemented"); } // toJson(Activity) is already JSON (hopefully that's obvious :') ), so replace it after converting the rest to JSON diff --git a/src/Core/DB.php b/src/Core/DB.php index ca48c0cfe9..523d8f2d28 100644 --- a/src/Core/DB.php +++ b/src/Core/DB.php @@ -50,6 +50,7 @@ use Functional as F; /** * @mixin EntityManager + * * @template T of Entity * * Finds an Entity by its identifier. You probably want to use DB::findBy instead. @@ -77,6 +78,9 @@ use Functional as F; * * Executes a function in a transaction. Warning: suppresses exceptions. Returns the result of the callable. * @method mixed wrapInTransaction(callable $func) + * + * Refetch the given entity + * @method static T refetch(T $entity) */ class DB { @@ -272,17 +276,17 @@ class DB { $res = self::findBy($table, $criteria, $order_by, 2, $offset); // Use limit 2 to check for consistency switch (\count($res)) { - case 0: - if ($return_null) { - return null; - } else { - throw new NotFoundException("No value in table {$table} matches the requested criteria"); - } - // no break - case 1: - return $res[0]; - default: - throw new DuplicateFoundException("Multiple values in table {$table} match the requested criteria"); + case 0: + if ($return_null) { + return null; + } else { + throw new NotFoundException("No value in table {$table} matches the requested criteria"); + } + // no break + case 1: + return $res[0]; + default: + throw new DuplicateFoundException("Multiple values in table {$table} match the requested criteria"); } } @@ -313,7 +317,8 @@ class DB $seqName = $metadata->getSequenceName($conn->getDatabasePlatform()); self::persist($owner); $id = (int) $conn->lastInsertId($seqName); - F\map(\is_array($others) ? $others : [$others], function ($o) use ($id) { $o->setId($id); self::persist($o); }); + F\map(\is_array($others) ? $others : [$others], function ($o) use ($id) { $o->setId($id); + self::persist($o); }); if (!\is_null($extra)) { $extra($id); } diff --git a/src/Core/Entity.php b/src/Core/Entity.php index 15fa447a2f..c09878d958 100644 --- a/src/Core/Entity.php +++ b/src/Core/Entity.php @@ -152,6 +152,8 @@ abstract class Entity * Entity::getByPK(['key1' => 42, 'key2' => 'foo']) * * @throws \App\Util\Exception\DuplicateFoundException + * + * @return $this */ public static function getByPK(mixed $values): ?self { diff --git a/src/Core/Event.php b/src/Core/Event.php index feb1d77c89..76a459b7ec 100644 --- a/src/Core/Event.php +++ b/src/Core/Event.php @@ -47,8 +47,9 @@ abstract class Event * EventResult::stop - Stop other handlers from processing the event * EventResult::next - Allow the other handlers to process the event */ - public const stop = EventResult::stop; - public const next = EventResult::next; + public const stop = EventResult::stop; + public const next = EventResult::next; + public const unhandled = EventResult::unhandled; private static EventDispatcherInterface $dispatcher; @@ -87,9 +88,9 @@ abstract class Event $ns . $name, function ($event, $event_name, $dispatcher) use ($handler) { $result = $handler(...$event->getArguments()); - if (\is_null($result) || !isset($result)) { - dd($handler, $event_name); - throw new BugFoundException("Events must return an \\EventResult, which a handler for {$event_name} does not"); + if (!isset($result)) { + $handler_file = (new ReflectionFunction($handler))->getFileName(); + throw new BugFoundException("Events must return an \\EventResult, which {$handler_file} for {$event_name} does not"); } $event->setResult($result); @@ -115,9 +116,6 @@ abstract class Event * @param string $name Name of the event that's happening * @param array $args Arguments for handlers * @param string $ns Namspace for the event - * - * @return bool flag saying whether to continue processing, based - * on results of handlers */ public static function handle(string $name, array $args = [], string $ns = 'GNUsocial.'): EventResult { diff --git a/src/Core/GSFile.php b/src/Core/GSFile.php index b1c74a69c2..29bb21d5cd 100644 --- a/src/Core/GSFile.php +++ b/src/Core/GSFile.php @@ -23,7 +23,6 @@ declare(strict_types = 1); namespace App\Core; -use App\Core\DB; use function App\Core\I18n\_m; use App\Util\Common; use App\Util\Exception\DuplicateFoundException; @@ -109,7 +108,7 @@ class GSFile // The following properly gets the mimetype with `file` or other // available methods, so should be safe $mimetype = mb_substr($file->getMimeType(), 0, 64); - $width = $height = null; + $width = $height = null; $event_map[$mimetype] = []; $major_mime = self::mimetypeMajor($mimetype); $event_map[$major_mime] = []; @@ -120,6 +119,7 @@ class GSFile } // Always prefer specific encoders $encoders = array_merge($event_map[$mimetype], $event_map[$major_mime]); + // @phpstan-ignore-next-line foreach ($encoders as $encoder) { if ($encoder($file, $mimetype, $width, $height)) { break; // One successful sanitizer is enough @@ -315,7 +315,7 @@ class GSFile $fallback = function ($title) use ($ext) { if (!\is_null($ext)) { - return ($title) . ".{$ext}"; + return $title . ".{$ext}"; } }; diff --git a/src/Core/ModuleManager.php b/src/Core/ModuleManager.php index ef23d2709e..d9832cda29 100644 --- a/src/Core/ModuleManager.php +++ b/src/Core/ModuleManager.php @@ -90,7 +90,6 @@ class ModuleManager function (string $method) use ($obj) { if (((string) (new ReflectionMethod($obj, $method))->getReturnType()) !== 'EventResult') { $class = $obj::class; - dd("Return type of {$class}::{$method} is not the required EventResult"); throw new BugFoundException("Return type of {$class}::{$method} is not the required EventResult"); } $event_name = mb_substr($method, 2); @@ -183,7 +182,7 @@ class ModuleManager } else { $rdi = new AppendIterator(); $rdi->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(INSTALLDIR . '/components', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS))); - $rdi->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(INSTALLDIR . '/plugins', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS))); + $rdi->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(INSTALLDIR . '/plugins', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS))); $time = file_exists(MODULE_CACHE_FILE) ? filemtime(MODULE_CACHE_FILE) : 0; if ($_ENV['APP_ENV'] === 'test' || F\some($rdi, fn ($e) => $e->getMTime() > $time)) { diff --git a/src/Core/Modules/NoteHandlerPlugin.php b/src/Core/Modules/NoteHandlerPlugin.php index c1b112751d..e032fa4ded 100644 --- a/src/Core/Modules/NoteHandlerPlugin.php +++ b/src/Core/Modules/NoteHandlerPlugin.php @@ -42,8 +42,6 @@ abstract class NoteHandlerPlugin extends Plugin * * @throws InvalidFormException * @throws NoSuchNoteException - * - * @return bool|void */ public static function noteActionHandle(Request $request, Form $form, Note $note, string $form_name, callable $handle) { diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index 2d5707b388..69a4c3f120 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -43,6 +43,7 @@ use Component\Language\Entity\ActorLanguage; use Component\Language\Entity\Language; use Component\Subscription\Entity\ActorSubscription; use Component\Tag\Tag; +use Component\Tag\Entity\NoteTag; use Functional as F; /** diff --git a/src/Entity/LocalUser.php b/src/Entity/LocalUser.php index 07216d7e5e..a46affae37 100644 --- a/src/Entity/LocalUser.php +++ b/src/Entity/LocalUser.php @@ -55,6 +55,10 @@ use Symfony\Component\Security\Core\User\UserInterface; * @author Hugo Sales * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + * + * + * @method bool hasNickname() + * @method bool hasPassword() */ class LocalUser extends Entity implements UserInterface, PasswordAuthenticatedUserInterface { diff --git a/src/Util/Common.php b/src/Util/Common.php index aae421f670..662e5a39ab 100644 --- a/src/Util/Common.php +++ b/src/Util/Common.php @@ -223,36 +223,36 @@ abstract class Common * * @return int the php.ini upload limit in machine-readable format */ - public static function sizeStrToInt(string $size): int + public static function sizeStrToInt(string|null $size): int { // `memory_limit` can be -1 and `post_max_size` can be 0 // for unlimited. Consistency. - if (empty($size) || $size === '-1' || $size === '0') { + if (!isset($size) || $size === '-1' || $size === '0') { $size = '3M'; } $suffix = mb_substr($size, -1); $size = (int) mb_substr($size, 0, -1); switch (mb_strtoupper($suffix)) { - case 'P': - $size *= 1024; - // no break - case 'T': - $size *= 1024; - // no break - case 'G': - $size *= 1024; - // no break - case 'M': - $size *= 1024; - // no break - case 'K': - $size *= 1024; - break; - default: - if ($suffix >= '0' && $suffix <= '9') { - $size = (int) "{$size}{$suffix}"; - } + case 'P': + $size *= 1024; + // no break + case 'T': + $size *= 1024; + // no break + case 'G': + $size *= 1024; + // no break + case 'M': + $size *= 1024; + // no break + case 'K': + $size *= 1024; + break; + default: + if ($suffix >= '0' && $suffix <= '9') { + $size = (int) "{$size}{$suffix}"; + } } return $size; } diff --git a/src/Util/Exception/InvalidRequestException.php b/src/Util/Exception/InvalidRequestException.php new file mode 100644 index 0000000000..c9b12ca1e8 --- /dev/null +++ b/src/Util/Exception/InvalidRequestException.php @@ -0,0 +1,34 @@ +. + +// }}} + +namespace App\Util\Exception; + +use function App\Core\I18n\_m; + +class InvalidRequestException extends ClientException +{ + public function __construct() + { + parent::__construct(_m('Invalid request')); + } +} diff --git a/src/Util/Form/ActorForms.php b/src/Util/Form/ActorForms.php index f26face835..ea44cc1134 100644 --- a/src/Util/Form/ActorForms.php +++ b/src/Util/Form/ActorForms.php @@ -121,7 +121,7 @@ class ActorForms foreach ($cache_keys as $key) { Cache::delete($key); } - Event::handle('ActorFormInvalidateRelated', [$target, $local]); + Event::handle('ActorFormInvalidateRelated', [$target, $local ?? null]); }; return Form::handle( diff --git a/src/Util/Formatting.php b/src/Util/Formatting.php index 94d5ad02c7..8bc0002edc 100644 --- a/src/Util/Formatting.php +++ b/src/Util/Formatting.php @@ -314,8 +314,7 @@ abstract class Formatting $mentions = []; // XXX: We remove because when content is in html the tag comes as #hashtag $text = str_replace('', '', $text); - if (Event::handle('StartFindMentions', [$actor, $text, &$mentions])) { - + if (Event::handle('StartFindMentions', [$actor, $text, &$mentions]) !== Event::stop) { // @person mentions $person_matches = self::findMentionsRaw($text, '@'); foreach ($person_matches as $match) { diff --git a/tests/Core/EntityTest.php b/tests/Core/EntityTest.php index 62b68b0428..111876b948 100644 --- a/tests/Core/EntityTest.php +++ b/tests/Core/EntityTest.php @@ -39,7 +39,7 @@ class EntityTest extends GNUsocialTestCase $user = LocalUser::create(['nickname' => 'foo']); static::assertTrue($user->hasNickname()); static::assertFalse($user->hasPassword()); - static::assertThrows(BadMethodCallException::class, fn () => $user->nonExistantMethod()); + static::assertThrows(BadMethodCallException::class, fn () => $user->nonExistantMethod()); // @phpstan-ignore-line } public function testCreate()