From 5c5c6da4be47b2464a5df420c290d703ba247386 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Thu, 6 May 2021 14:00:48 +0000
Subject: [PATCH] refactor: add rector to enforce type declarations, code
 quality + style and remove dead code

- update CI process to include quality stage (tests + code review)
- add captainhook to install git pre-commit & pre-push hooks
- remove .devcontainer Dockerfile to use project's docker-compose services: all
services can now be started automatically using vscode
- update docs/setup-development.md
---
 .devcontainer/Dockerfile                      |   12 -
 .devcontainer/devcontainer.json               |   35 +-
 .gitlab-ci.yml                                |   27 +-
 .releaserc.json                               |    2 +-
 Dockerfile                                    |   23 +
 app/Authorization/FlatAuthorization.php       |   47 +-
 app/Authorization/GroupModel.php              |   10 +-
 app/Authorization/PermissionModel.php         |    9 +-
 app/Config/Analytics.php                      |    5 +-
 app/Config/App.php                            |   13 +-
 app/Config/Auth.php                           |   54 +-
 app/Config/ContentSecurityPolicy.php          |   18 +-
 app/Config/Database.php                       |    1 +
 app/Config/Events.php                         |   28 +-
 app/Config/Filters.php                        |   12 +-
 app/Config/Format.php                         |   15 +-
 app/Config/Kint.php                           |   53 +-
 app/Config/Logger.php                         |    3 +-
 app/Config/Mimes.php                          |   10 +-
 app/Config/Routes.php                         |   52 +-
 app/Controllers/Actor.php                     |    2 +-
 app/Controllers/Admin/BaseController.php      |    6 +-
 app/Controllers/Admin/Contributor.php         |   50 +-
 app/Controllers/Admin/Episode.php             |    9 +-
 app/Controllers/Admin/EpisodePerson.php       |   50 +-
 app/Controllers/Admin/Fediverse.php           |    2 -
 app/Controllers/Admin/Page.php                |   20 +-
 app/Controllers/Admin/Person.php              |   26 +-
 app/Controllers/Admin/Podcast.php             |   33 +-
 app/Controllers/Admin/PodcastImport.php       |  125 +-
 app/Controllers/Admin/PodcastPerson.php       |   24 +-
 app/Controllers/Admin/PodcastPlatform.php     |   69 +-
 app/Controllers/Admin/User.php                |   34 +-
 app/Controllers/Auth.php                      |    8 +-
 app/Controllers/BaseController.php            |    6 +-
 app/Controllers/Episode.php                   |   31 +-
 app/Controllers/Feed.php                      |   16 +-
 app/Controllers/Home.php                      |    3 +
 app/Controllers/Install.php                   |   67 +-
 app/Controllers/Note.php                      |   14 +-
 app/Controllers/Page.php                      |   34 +-
 app/Controllers/Platform.php                  |    6 +-
 app/Controllers/Podcast.php                   |   20 +-
 .../2020-05-29-152000_add_categories.php      |    4 +-
 .../2020-05-30-101000_add_languages.php       |    4 +-
 .../2020-05-30-101500_add_podcasts.php        |    4 +-
 .../2020-06-05-170000_add_episodes.php        |    4 +-
 .../2020-06-05-180000_add_soundbites.php      |    4 +-
 .../2020-06-05-190000_add_platforms.php       |    6 +-
 .../2020-07-03-191500_add_podcasts_users.php  |    4 +-
 .../2020-08-17-150000_add_pages.php           |    4 +-
 ...0-09-29-150000_add_podcasts_categories.php |    4 +-
 .../2020-12-25-120000_add_persons.php         |    4 +-
 ...2020-12-25-130000_add_podcasts_persons.php |    4 +-
 ...2020-12-25-140000_add_episodes_persons.php |    4 +-
 .../2020-12-25-150000_add_credit_view.php     |   30 +-
 ...1-02-23-100000_add_episode_id_to_notes.php |    8 +-
 ...1-03-09-113000_add_created_by_to_notes.php |    8 +-
 app/Database/Seeds/AppSeeder.php              |    2 +-
 app/Database/Seeds/AuthSeeder.php             |   41 +-
 app/Database/Seeds/CategorySeeder.php         |    2 +-
 .../Seeds/FakePodcastsAnalyticsSeeder.php     |   14 +-
 .../Seeds/FakeWebsiteAnalyticsSeeder.php      |   14 +-
 app/Database/Seeds/LanguageSeeder.php         |    6 +-
 app/Database/Seeds/PlatformSeeder.php         |    2 +-
 app/Database/Seeds/TestSeeder.php             |    3 +-
 app/Entities/Actor.php                        |    3 +-
 app/Entities/Category.php                     |   17 +-
 app/Entities/Credit.php                       |   56 +-
 app/Entities/Episode.php                      |  120 +-
 app/Entities/EpisodePerson.php                |    9 +-
 app/Entities/Language.php                     |    5 +-
 app/Entities/Note.php                         |    5 +-
 app/Entities/Page.php                         |    9 +-
 app/Entities/Person.php                       |   23 +-
 app/Entities/Platform.php                     |    5 +-
 app/Entities/Podcast.php                      |  210 +-
 app/Entities/PodcastPerson.php                |    9 +-
 app/Entities/Soundbite.php                    |    7 +-
 app/Entities/User.php                         |   28 +-
 app/Filters/PermissionFilter.php              |   27 +-
 app/Helpers/auth_helper.php                   |   20 +-
 app/Helpers/breadcrumb_helper.php             |   32 +-
 app/Helpers/components_helper.php             |  138 +-
 app/Helpers/form_helper.php                   |   25 +-
 app/Helpers/id3_helper.php                    |  198 +-
 app/Helpers/location_helper.php               |   85 +-
 app/Helpers/media_helper.php                  |  209 +-
 app/Helpers/misc_helper.php                   |  265 +-
 app/Helpers/page_helper.php                   |   39 +-
 app/Helpers/persons_helper.php                |   76 +-
 app/Helpers/rss_helper.php                    |  889 +--
 app/Helpers/svg_helper.php                    |   74 +-
 app/Helpers/url_helper.php                    |   65 +-
 app/Language/en/ActivityPub.php               |    6 +-
 app/Language/en/Contributor.php               |    6 +-
 app/Language/en/Countries.php                 |    6 +-
 app/Language/en/Episode.php                   |    2 +-
 app/Language/en/Install.php                   |    4 +-
 app/Language/en/MyAccount.php                 |    3 +-
 app/Language/en/Note.php                      |    2 +-
 app/Language/en/User.php                      |    6 +-
 app/Language/fr/Countries.php                 |    4 +-
 app/Language/fr/Episode.php                   |    9 +-
 app/Language/fr/PodcastNavigation.php         |    1 -
 app/Libraries/ActivityPub/ActivityRequest.php |   31 +-
 .../ActivityPub/Config/ActivityPub.php        |   18 +
 app/Libraries/ActivityPub/Config/Routes.php   |    4 +-
 .../Controllers/ActorController.php           |  123 +-
 .../Controllers/BlockController.php           |    5 +-
 .../Controllers/NoteController.php            |   35 +-
 .../Controllers/SchedulerController.php       |    7 +-
 .../Controllers/WebFingerController.php       |    8 +-
 .../ActivityPub/Core/AbstractObject.php       |   22 +-
 app/Libraries/ActivityPub/Core/Activity.php   |    2 +-
 app/Libraries/ActivityPub/Core/ObjectType.php |    2 +-
 .../2018-01-01-010000_add_actors.php          |    4 +-
 .../2018-01-01-020000_add_notes.php           |    4 +-
 .../2018-01-01-100000_add_activities.php      |    4 +-
 .../2018-01-01-100000_add_favourites.php      |    4 +-
 .../2018-01-01-100000_add_follows.php         |    4 +-
 .../2018-01-01-100000_add_preview_cards.php   |    4 +-
 ...8-01-01-110000_add_notes_preview_cards.php |    4 +-
 .../2018-01-01-120000_add_blocked_domains.php |    4 +-
 .../ActivityPub/Entities/Activity.php         |   37 +-
 app/Libraries/ActivityPub/Entities/Actor.php  |   35 +-
 .../ActivityPub/Entities/BlockedDomain.php    |    5 +-
 .../ActivityPub/Entities/Favourite.php        |    6 +
 app/Libraries/ActivityPub/Entities/Follow.php |    5 +-
 app/Libraries/ActivityPub/Entities/Note.php   |   88 +-
 .../ActivityPub/Entities/PreviewCard.php      |    5 +-
 .../ActivityPub/Filters/ActivityPubFilter.php |   24 +-
 .../Helpers/activitypub_helper.php            |  163 +-
 app/Libraries/ActivityPub/HttpSignature.php   |   31 +-
 .../ActivityPub/Models/ActivityModel.php      |   50 +-
 .../ActivityPub/Models/ActorModel.php         |   48 +-
 .../ActivityPub/Models/BlockedDomainModel.php |   32 +-
 .../ActivityPub/Models/FavouriteModel.php     |   55 +-
 .../ActivityPub/Models/FollowModel.php        |   46 +-
 .../ActivityPub/Models/NoteModel.php          |  141 +-
 .../ActivityPub/Models/PreviewCardModel.php   |   27 +-
 .../ActivityPub/Objects/ActorObject.php       |    8 +-
 .../ActivityPub/Objects/NoteObject.php        |    5 +-
 .../Objects/OrderedCollectionObject.php       |   18 +-
 .../Objects/OrderedCollectionPage.php         |   15 +-
 app/Libraries/ActivityPub/WebFinger.php       |   44 +-
 app/Libraries/Analytics/AnalyticsTrait.php    |   15 +-
 app/Libraries/Analytics/Config/Analytics.php  |    6 +-
 app/Libraries/Analytics/Config/Routes.php     |    4 +-
 .../Controllers/AnalyticsController.php       |   14 +-
 .../EpisodeAnalyticsController.php            |   18 +-
 .../UnknownUserAgentsController.php           |    3 +-
 ...17-12-01-120000_add_analytics_podcasts.php |    4 +-
 ...0000_add_analytics_podcasts_by_episode.php |    4 +-
 ...-130000_add_analytics_podcasts_by_hour.php |    4 +-
 ...40000_add_analytics_podcasts_by_player.php |    4 +-
 ...0000_add_analytics_podcasts_by_country.php |    4 +-
 ...60000_add_analytics_podcasts_by_region.php |    4 +-
 ...17-12-01-160000_add_podcasts_platforms.php |    4 +-
 ...70000_add_analytics_website_by_browser.php |    4 +-
 ...80000_add_analytics_website_by_referer.php |    4 +-
 ...00_add_analytics_website_by_entry_page.php |    4 +-
 ...00000_add_analytics_unknown_useragents.php |    4 +-
 ...10000_add_analytics_podcasts_procedure.php |    4 +-
 ...analytics_unknown_useragents_procedure.php |   12 +-
 ...210000_add_analytics_website_procedure.php |    8 +-
 .../Analytics/Entities/AnalyticsPodcasts.php  |    6 +-
 .../Entities/AnalyticsPodcastsByCountry.php   |    6 +-
 .../Entities/AnalyticsPodcastsByEpisode.php   |    6 +-
 .../Entities/AnalyticsPodcastsByHour.php      |    6 +-
 .../Entities/AnalyticsPodcastsByPlayer.php    |    6 +-
 .../Entities/AnalyticsPodcastsByRegion.php    |    6 +-
 .../Entities/AnalyticsPodcastsByService.php   |   12 +-
 .../Entities/AnalyticsUnknownUseragents.php   |    5 +-
 .../Entities/AnalyticsWebsiteByBrowser.php    |    6 +-
 .../Entities/AnalyticsWebsiteByEntryPage.php  |    6 +-
 .../Entities/AnalyticsWebsiteByReferer.php    |    6 +-
 .../Analytics/Helpers/analytics_helper.php    |  140 +-
 .../Models/AnalyticsPodcastByCountryModel.php |   26 +-
 .../Models/AnalyticsPodcastByEpisodeModel.php |   29 +-
 .../Models/AnalyticsPodcastByHourModel.php    |   26 +-
 .../Models/AnalyticsPodcastByPlayerModel.php  |   37 +-
 .../Models/AnalyticsPodcastByRegionModel.php  |   21 +-
 .../Models/AnalyticsPodcastByServiceModel.php |   21 +-
 .../Models/AnalyticsPodcastModel.php          |   49 +-
 .../AnalyticsUnknownUseragentsModel.php       |   20 +-
 .../Models/AnalyticsWebsiteByBrowserModel.php |   21 +-
 .../AnalyticsWebsiteByEntryPageModel.php      |   23 +-
 .../Models/AnalyticsWebsiteByRefererModel.php |   37 +-
 .../Models/UnknownUserAgentsModel.php         |    5 +-
 app/Libraries/Breadcrumb.php                  |   12 +-
 app/Libraries/Image.php                       |   40 +-
 app/Libraries/NoteObject.php                  |    2 +-
 app/Libraries/PodcastActor.php                |    6 +-
 app/Libraries/Router.php                      |    3 +-
 app/Libraries/SimpleRSSElement.php            |   14 +-
 app/Models/CategoryModel.php                  |   74 +-
 app/Models/CreditModel.php                    |   11 +-
 app/Models/EpisodeModel.php                   |  157 +-
 app/Models/EpisodePersonModel.php             |   54 +-
 app/Models/LanguageModel.php                  |   23 +-
 app/Models/PageModel.php                      |   44 +-
 app/Models/PersonModel.php                    |   53 +-
 app/Models/PlatformModel.php                  |   42 +-
 app/Models/PodcastModel.php                   |    6 +-
 app/Models/PodcastPersonModel.php             |   57 +-
 app/Models/SoundbiteModel.php                 |   44 +-
 app/Models/UserModel.php                      |    6 +-
 app/Validation/FileRules.php                  |    5 -
 app/Validation/Rules.php                      |    3 -
 app/Views/_assets/modules/Charts.ts           |   95 +-
 app/Views/_assets/modules/Soundbites.ts       |    3 +
 app/Views/admin/_partials/_user_info.php      |    2 +-
 app/Views/admin/contributor/add.php           |    4 +-
 app/Views/admin/contributor/edit.php          |    4 +-
 app/Views/admin/contributor/list.php          |   15 +-
 app/Views/admin/episode/create.php            |   12 +-
 app/Views/admin/episode/edit.php              |   15 +-
 app/Views/admin/episode/embeddable_player.php |    4 +-
 app/Views/admin/episode/person.php            |   35 +-
 app/Views/admin/episode/publish.php           |    7 +-
 app/Views/admin/episode/publish_edit.php      |    4 +-
 app/Views/admin/episode/soundbites.php        |    4 +-
 app/Views/admin/episode/unpublish.php         |    2 +-
 app/Views/admin/episode/view.php              |   12 +-
 app/Views/admin/fediverse/blocked_actors.php  |    2 +-
 app/Views/admin/fediverse/blocked_domains.php |    2 +-
 .../admin/my_account/change_password.php      |    5 +-
 app/Views/admin/page/create.php               |    6 +-
 app/Views/admin/page/edit.php                 |    6 +-
 app/Views/admin/page/list.php                 |    8 +-
 app/Views/admin/person/create.php             |   14 +-
 app/Views/admin/person/edit.php               |   14 +-
 app/Views/admin/person/list.php               |   18 +-
 app/Views/admin/person/view.php               |    2 +-
 .../podcast/analytics/listening_time.php      |    4 +-
 .../admin/podcast/analytics/locations.php     |    6 +-
 app/Views/admin/podcast/analytics/players.php |   10 +-
 .../admin/podcast/analytics/time_periods.php  |    4 +-
 .../podcast/analytics/unique_listeners.php    |    4 +-
 .../admin/podcast/analytics/webpages.php      |    8 +-
 app/Views/admin/podcast/create.php            |   75 +-
 app/Views/admin/podcast/edit.php              |    2 +-
 app/Views/admin/podcast/import.php            |   17 +-
 app/Views/admin/podcast/person.php            |  128 +-
 app/Views/admin/podcast/platforms.php         |    2 +-
 app/Views/admin/user/create.php               |    5 +-
 app/Views/admin/user/edit.php                 |    4 +-
 app/Views/admin/user/list.php                 |   12 +-
 app/Views/auth/emails/activation.php          |    2 +-
 app/Views/auth/forgot.php                     |    4 +-
 app/Views/auth/login.php                      |    8 +-
 app/Views/auth/register.php                   |    8 +-
 app/Views/auth/reset.php                      |    4 +-
 app/Views/credits.php                         |   72 +-
 app/Views/embeddable_player.php               |    2 +-
 app/Views/errors/cli/error_exception.php      |   10 +-
 app/Views/errors/html/error_404.php           |   30 +-
 app/Views/errors/html/error_exception.php     |  324 +-
 app/Views/errors/html/production.php          |    4 +-
 app/Views/install/cache_config.php            |    2 +-
 app/Views/install/create_superadmin.php       |    2 +-
 app/Views/install/database_config.php         |    2 +-
 app/Views/install/instance_config.php         |    2 +-
 app/Views/page.php                            |    2 +-
 app/Views/pager/default_full.php              |   17 +-
 app/Views/podcast/_layout_authenticated.php   |    2 +-
 app/Views/podcast/_partials/header.php        |    2 +-
 app/Views/podcast/_partials/note.php          |   12 +-
 .../_partials/note_actions_authenticated.php  |    2 +-
 .../podcast/_partials/note_authenticated.php  |   12 +-
 .../note_with_replies_authenticated.php       |    2 +-
 app/Views/podcast/_partials/reblog.php        |   12 +-
 .../_partials/reblog_authenticated.php        |   12 +-
 app/Views/podcast/_partials/reply.php         |    8 +-
 app/Views/podcast/_partials/reply_actions.php |    2 +-
 .../_partials/reply_actions_authenticated.php |    2 +-
 .../podcast/_partials/reply_authenticated.php |    8 +-
 app/Views/podcast/_partials/sidebar.php       |   10 +-
 app/Views/podcast/activity_authenticated.php  |    2 +-
 app/Views/podcast/episode_authenticated.php   |    2 +-
 app/Views/podcast/follow.php                  |    4 +-
 app/Views/podcast/note_remote_action.php      |    2 +-
 captainhook.json                              |   37 +
 composer.json                                 |   14 +-
 composer.lock                                 | 6223 ++++++++++++++---
 docker-compose.yml                            |   28 +-
 docs/setup-development.md                     |  365 +-
 package.json                                  |    1 +
 phpunit.xml.dist                              |    2 +-
 rector.php                                    |   67 +
 .../prepare-release.sh                        |    0
 tests/README.md                               |    6 +-
 .../2020-02-22-222222_example_migration.php   |    7 +-
 .../_support/Database/Seeds/ExampleSeeder.php |    2 +-
 tests/_support/DatabaseTestCase.php           |   11 +-
 tests/_support/Libraries/ConfigReader.php     |    7 +-
 tests/_support/Models/ExampleModel.php        |   27 +
 tests/_support/SessionTestCase.php            |    8 +-
 tests/database/ExampleDatabaseTest.php        |    9 +-
 tests/session/ExampleSessionTest.php          |    7 +-
 tests/unit/HealthTest.php                     |   40 +-
 302 files changed, 9802 insertions(+), 4674 deletions(-)
 delete mode 100644 .devcontainer/Dockerfile
 create mode 100644 captainhook.json
 create mode 100644 rector.php
 rename prepare-release.sh => scripts/prepare-release.sh (100%)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
deleted file mode 100644
index aa74483c2a..0000000000
--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM php:7.3-fpm
-
-COPY --from=composer /usr/bin/composer /usr/bin/composer
-
-RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
-
-RUN apt-get update && \
-    apt-get install -y nodejs
-
-RUN apt-get update && \
-    apt-get upgrade -y && \
-    apt-get install -y git vim
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 45fcd1c022..beb4929c79 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,8 +1,11 @@
 // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
 // https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/docker-existing-dockerfile
 {
-  "name": "Existing Dockerfile",
-  "dockerFile": "./Dockerfile",
+  "name": "Castopod Host dev",
+  "dockerComposeFile": ["../docker-compose.yml"],
+  "service": "app",
+  "workspaceFolder": "/castopod-host",
+  "postCreateCommand": "cron && php spark serve --host 0.0.0.0",
   "settings": {
     "terminal.integrated.shell.linux": "/bin/bash",
     "editor.formatOnSave": true,
@@ -13,18 +16,18 @@
     "color-highlight.markerType": "dot-before"
   },
   "extensions": [
-	"mikestead.dotenv",
-	"bmewburn.vscode-intelephense-client",
-	"streetsidesoftware.code-spell-checker",
-	"naumovs.color-highlight",
-	"heybourn.headwind",
-	"wayou.vscode-todo-highlight",
-	"esbenp.prettier-vscode",
-	"bradlc.vscode-tailwindcss",
-	"jamesbirtles.svelte-vscode",
-	"dbaeumer.vscode-eslint",
-	"stylelint.vscode-stylelint",
-	"wongjn.php-sniffer",
-	"eamodio.gitlens"
-]
+    "mikestead.dotenv",
+    "bmewburn.vscode-intelephense-client",
+    "streetsidesoftware.code-spell-checker",
+    "naumovs.color-highlight",
+    "heybourn.headwind",
+    "wayou.vscode-todo-highlight",
+    "esbenp.prettier-vscode",
+    "bradlc.vscode-tailwindcss",
+    "jamesbirtles.svelte-vscode",
+    "dbaeumer.vscode-eslint",
+    "stylelint.vscode-stylelint",
+    "wongjn.php-sniffer",
+    "eamodio.gitlens"
+  ]
 }
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c295d6fb39..f93745e612 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,7 @@
 image: php:7.3-fpm
 
 stages:
+  - quality
   - bundle
   - release
 
@@ -31,16 +32,30 @@ before_script:
   - curl -sL https://deb.nodesource.com/setup_12.x | bash -
   - apt-get update && apt-get install -y nodejs
 
-  # Install php and js dependencies
-  - php composer.phar install --no-dev --ignore-platform-reqs
+  # Install all php and js dependencies
+  - php composer.phar install --prefer-dist --no-ansi --no-interaction --no-progress --ignore-platform-reqs
   - npm install
 
-  # build all UI assets
-  - npm run build
+tests:
+  stage: quality
+  script:
+    - vendor/bin/phpunit
+
+code-review:
+  stage: quality
+  script:
+    # run rector
+    - vendor/bin/rector process --dry-run
 
 bundle_app:
   stage: bundle
   script:
+    # remove dev dependencies using the --no-dev option
+    - php composer.phar install --no-dev --prefer-dist --no-ansi --no-interaction --no-progress --ignore-platform-reqs
+
+    # build all UI assets
+    - npm run build
+
     # download GeoLite2-City archive and extract it to writable/uploads
     - wget -c "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=$MAXMIND_LICENCE_KEY&suffix=tar.gz" -O - | tar -xz -C ./writable/uploads/
 
@@ -65,8 +80,8 @@ release_app:
     - apt-get install jq -y
     - apt-get install zip -y
 
-    # make prepare-release.sh executable
-    - chmod +x ./prepare-release.sh
+    # make scripts/prepare-release.sh executable
+    - chmod +x ./scripts/prepare-release.sh
 
     # IMPORTANT: delete local git tags before release to prevent eventual script failure (ie. tag already exists)
     - git tag | xargs git tag -d
diff --git a/.releaserc.json b/.releaserc.json
index 8c76839dab..018006c294 100644
--- a/.releaserc.json
+++ b/.releaserc.json
@@ -11,7 +11,7 @@
     [
       "@semantic-release/exec",
       {
-        "prepareCmd": "./prepare-release.sh ${nextRelease.version}"
+        "prepareCmd": "./scripts/prepare-release.sh ${nextRelease.version}"
       }
     ],
     "@semantic-release/npm",
diff --git a/Dockerfile b/Dockerfile
index 8792d21dc6..fce76183d6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,31 @@
+####################################################
+# Castopod Host development Docker file
+####################################################
+# NOT optimized for production
+# should be used only for development purposes
+####################################################
+
 FROM php:7.3-fpm
 
+LABEL maintainer="Yassine Doghri<yassine@podlibre.org>"
+
 COPY . /castopod-host
 WORKDIR /castopod-host
 
+# Install composer
+COPY --from=composer /usr/bin/composer /usr/bin/composer
+
+# Install npm
+RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
+
+RUN apt-get update && \
+    apt-get install -y nodejs
+
+# Install git + vim
+RUN apt-get update && \
+    apt-get upgrade -y && \
+    apt-get install -y git vim
+
 ### Install CodeIgniter's server requirements
 #-- https://github.com/codeigniter4/appstarter#server-requirements
 
diff --git a/app/Authorization/FlatAuthorization.php b/app/Authorization/FlatAuthorization.php
index f96fb2aa7b..5c4a7f8980 100644
--- a/app/Authorization/FlatAuthorization.php
+++ b/app/Authorization/FlatAuthorization.php
@@ -4,31 +4,13 @@ namespace App\Authorization;
 
 class FlatAuthorization extends \Myth\Auth\Authorization\FlatAuthorization
 {
-    //--------------------------------------------------------------------
-    // Actions
-    //--------------------------------------------------------------------
-
     /**
      * Checks a group to see if they have the specified permission.
      *
      * @param int|string $permission
-     * @param int        $groupId
-     *
-     * @return mixed
      */
-    public function groupHasPermission($permission, int $groupId)
+    public function groupHasPermission($permission, int $groupId): bool
     {
-        if (
-            empty($permission) ||
-            (!is_string($permission) && !is_numeric($permission))
-        ) {
-            return null;
-        }
-
-        if (empty($groupId) || !is_numeric($groupId)) {
-            return null;
-        }
-
         // Get the Permission ID
         $permissionId = $this->getPermissionID($permission);
 
@@ -36,36 +18,23 @@ class FlatAuthorization extends \Myth\Auth\Authorization\FlatAuthorization
             return false;
         }
 
-        if (
-            $this->permissionModel->doesGroupHavePermission(
-                $groupId,
-                (int) $permissionId
-            )
-        ) {
-            return true;
-        }
-
-        return false;
+        return (bool) $this->permissionModel->doesGroupHavePermission(
+            $groupId,
+            $permissionId,
+        );
     }
 
     /**
      * Makes user part of given groups.
      *
-     * @param $userId
-     * @param array|null $groups // Either collection of ID or names
-     *
-     * @return bool
+     * @param array $groups Either collection of ID or names
      */
-    public function setUserGroups(int $userId, $groups)
+    public function setUserGroups(int $userId, array $groups = []): bool
     {
-        if (empty($userId) || !is_numeric($userId)) {
-            return null;
-        }
-
         // remove user from all groups before resetting it in new groups
         $this->groupModel->removeUserFromAllGroups($userId);
 
-        if (empty($groups)) {
+        if ($groups = []) {
             return true;
         }
 
diff --git a/app/Authorization/GroupModel.php b/app/Authorization/GroupModel.php
index 74455708d7..4c7da3f6ac 100644
--- a/app/Authorization/GroupModel.php
+++ b/app/Authorization/GroupModel.php
@@ -4,14 +4,20 @@ namespace App\Authorization;
 
 class GroupModel extends \Myth\Auth\Authorization\GroupModel
 {
-    public function getContributorRoles()
+    /**
+     * @return mixed[]
+     */
+    public function getContributorRoles(): array
     {
         return $this->select('auth_groups.*')
             ->like('name', 'podcast_', 'after')
             ->findAll();
     }
 
-    public function getUserRoles()
+    /**
+     * @return mixed[]
+     */
+    public function getUserRoles(): array
     {
         return $this->select('auth_groups.*')
             ->notLike('name', 'podcast_', 'after')
diff --git a/app/Authorization/PermissionModel.php b/app/Authorization/PermissionModel.php
index c15f4412b6..254265e992 100644
--- a/app/Authorization/PermissionModel.php
+++ b/app/Authorization/PermissionModel.php
@@ -7,11 +7,6 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
     /**
      * Checks to see if a user, or one of their groups,
      * has a specific permission.
-     *
-     * @param $userId
-     * @param $permissionId
-     *
-     * @return bool
      */
     public function doesGroupHavePermission(
         int $groupId,
@@ -33,9 +28,7 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
      *  id => name
      * ]
      *
-     * @param int $groupId
-     *
-     * @return array
+     * @return array<int, string>
      */
     public function getPermissionsForGroup(int $groupId): array
     {
diff --git a/app/Config/Analytics.php b/app/Config/Analytics.php
index c349725297..19b7555981 100644
--- a/app/Config/Analytics.php
+++ b/app/Config/Analytics.php
@@ -26,7 +26,10 @@ class Analytics extends AnalyticsBase
         $this->gateway = config('App')->adminGateway . '/analytics';
     }
 
-    public function getAudioFileUrl($audioFilePath)
+    /**
+     * get the full audio file url
+     */
+    public function getAudioFileUrl(string $audioFilePath): string
     {
         helper('media');
 
diff --git a/app/Config/App.php b/app/Config/App.php
index f08c514118..610960079f 100644
--- a/app/Config/App.php
+++ b/app/Config/App.php
@@ -2,6 +2,7 @@
 
 namespace Config;
 
+use CodeIgniter\Session\Handlers\FileHandler;
 use CodeIgniter\Config\BaseConfig;
 
 class App extends BaseConfig
@@ -34,6 +35,8 @@ class App extends BaseConfig
      * WITH a trailing slash:
      *
      *    http://cdn.example.com/
+     *
+     * @var string
      */
     public $mediaBaseURL = 'http://127.0.0.2:8080/';
 
@@ -163,7 +166,7 @@ class App extends BaseConfig
      *
      * @var string
      */
-    public $sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler';
+    public $sessionDriver = FileHandler::class;
 
     /**
      * --------------------------------------------------------------------------
@@ -480,6 +483,8 @@ class App extends BaseConfig
      * Media root folder
      * --------------------------------------------------------------------------
      * Defines the root folder for media files storage
+     *
+     * @var string
      */
     public $mediaRoot = 'media';
 
@@ -488,6 +493,8 @@ class App extends BaseConfig
      * Admin gateway
      * --------------------------------------------------------------------------
      * Defines a base route for all admin pages
+     *
+     * @var string
      */
     public $adminGateway = 'cp-admin';
 
@@ -496,6 +503,8 @@ class App extends BaseConfig
      * Auth gateway
      * --------------------------------------------------------------------------
      * Defines a base route for all authentication related pages
+     *
+     * @var string
      */
     public $authGateway = 'cp-auth';
 
@@ -504,6 +513,8 @@ class App extends BaseConfig
      * Install gateway
      * --------------------------------------------------------------------------
      * Defines a base route for instance installation
+     *
+     * @var string
      */
     public $installGateway = 'cp-install';
 }
diff --git a/app/Config/Auth.php b/app/Config/Auth.php
index 6c5b78f5a2..db08a27afb 100644
--- a/app/Config/Auth.php
+++ b/app/Config/Auth.php
@@ -4,10 +4,13 @@ namespace Config;
 
 class Auth extends \Myth\Auth\Config\Auth
 {
-    //--------------------------------------------------------------------
-    // Views used by Auth Controllers
-    //--------------------------------------------------------------------
-
+    /**
+     * --------------------------------------------------------------------------
+     * Views used by Auth Controllers
+     * --------------------------------------------------------------------------
+     *
+     * @var array<string, string>
+     */
     public $views = [
         'login' => 'auth/login',
         'register' => 'auth/register',
@@ -17,26 +20,35 @@ class Auth extends \Myth\Auth\Config\Auth
         'emailActivation' => 'auth/emails/activation',
     ];
 
-    //--------------------------------------------------------------------
-    // Layout for the views to extend
-    //--------------------------------------------------------------------
-
+    /**
+     * --------------------------------------------------------------------------
+     * Layout for the views to extend
+     * --------------------------------------------------------------------------
+     *
+     * @var string
+     */
     public $viewLayout = 'auth/_layout';
 
-    //--------------------------------------------------------------------
-    // Allow User Registration
-    //--------------------------------------------------------------------
-    // When enabled (default) any unregistered user may apply for a new
-    // account. If you disable registration you may need to ensure your
-    // controllers and views know not to offer registration.
-    //
+    /**
+     * --------------------------------------------------------------------------
+     * Allow User Registration
+     * --------------------------------------------------------------------------
+     * When enabled (default) any unregistered user may apply for a new
+     * account. If you disable registration you may need to ensure your
+     * controllers and views know not to offer registration.
+     *
+     * @var bool
+     */
     public $allowRegistration = false;
 
-    //--------------------------------------------------------------------
-    // Require confirmation registration via email
-    //--------------------------------------------------------------------
-    // When enabled, every registered user will receive an email message
-    // with a special link he have to confirm to activate his account.
-    //
+    /**
+     * --------------------------------------------------------------------------
+     * Require confirmation registration via email
+     * --------------------------------------------------------------------------
+     * When enabled, every registered user will receive an email message
+     * with a special link he have to confirm to activate his account.
+     *
+     * @var bool
+     */
     public $requireActivation = false;
 }
diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php
index 7caa542270..05ffa5d7c7 100644
--- a/app/Config/ContentSecurityPolicy.php
+++ b/app/Config/ContentSecurityPolicy.php
@@ -32,7 +32,7 @@ class ContentSecurityPolicy extends BaseConfig
      *
      * @var string|null
      */
-    public $reportURI = null;
+    public $reportURI;
 
     /**
      * Instructs user agents to rewrite URL schemes, changing
@@ -53,7 +53,7 @@ class ContentSecurityPolicy extends BaseConfig
      *
      * @var string|string[]|null
      */
-    public $defaultSrc = null;
+    public $defaultSrc;
 
     /**
      * Lists allowed scripts' URLs.
@@ -83,7 +83,7 @@ class ContentSecurityPolicy extends BaseConfig
      *
      * @var string|string[]|null
      */
-    public $baseURI = null;
+    public $baseURI;
 
     /**
      * Lists the URLs for workers and embedded frame contents
@@ -105,7 +105,7 @@ class ContentSecurityPolicy extends BaseConfig
      *
      * @var string|string[]
      */
-    public $fontSrc = null;
+    public $fontSrc;
 
     /**
      * Lists valid endpoints for submission from `<form>` tags.
@@ -122,14 +122,14 @@ class ContentSecurityPolicy extends BaseConfig
      *
      * @var string|string[]|null
      */
-    public $frameAncestors = null;
+    public $frameAncestors;
 
     /**
      * Restricts the origins allowed to deliver video and audio.
      *
      * @var string|string[]|null
      */
-    public $mediaSrc = null;
+    public $mediaSrc;
 
     /**
      * Allows control over Flash and other plugins.
@@ -141,19 +141,19 @@ class ContentSecurityPolicy extends BaseConfig
     /**
      * @var string|string[]|null
      */
-    public $manifestSrc = null;
+    public $manifestSrc;
 
     /**
      * Limits the kinds of plugins a page may invoke.
      *
      * @var string|string[]|null
      */
-    public $pluginTypes = null;
+    public $pluginTypes;
 
     /**
      * List of actions allowed.
      *
      * @var string|string[]|null
      */
-    public $sandbox = null;
+    public $sandbox;
 }
diff --git a/app/Config/Database.php b/app/Config/Database.php
index f655132c29..4ee26d9513 100644
--- a/app/Config/Database.php
+++ b/app/Config/Database.php
@@ -62,6 +62,7 @@ class Database extends Config
         'username' => '',
         'password' => '',
         'database' => ':memory:',
+        /** @noRector StringClassNameToClassConstantRector */
         'DBDriver' => 'SQLite3',
         'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
         'pConnect' => false,
diff --git a/app/Config/Events.php b/app/Config/Events.php
index da21d27df3..6a99445052 100644
--- a/app/Config/Events.php
+++ b/app/Config/Events.php
@@ -52,7 +52,7 @@ Events::on('pre_system', function () {
     }
 });
 
-Events::on('login', function ($user) {
+Events::on('login', function ($user): void {
     helper('auth');
 
     // set interact_as_actor_id value
@@ -62,7 +62,7 @@ Events::on('login', function ($user) {
     }
 });
 
-Events::on('logout', function ($user) {
+Events::on('logout', function ($user): void {
     helper('auth');
 
     // remove user's interact_as_actor session
@@ -75,7 +75,7 @@ Events::on('logout', function ($user) {
  * --------------------------------------------------------------------
  * Update episode metadata counts
  */
-Events::on('on_note_add', function ($note) {
+Events::on('on_note_add', function ($note): void {
     if ($note->episode_id) {
         model('EpisodeModel')
             ->where('id', $note->episode_id)
@@ -87,7 +87,7 @@ Events::on('on_note_add', function ($note) {
     cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
 });
 
-Events::on('on_note_remove', function ($note) {
+Events::on('on_note_remove', function ($note): void {
     if ($note->episode_id) {
         model('EpisodeModel')
             ->where('id', $note->episode_id)
@@ -106,7 +106,7 @@ Events::on('on_note_remove', function ($note) {
     cache()->deleteMatching("page_note#{$note->id}*");
 });
 
-Events::on('on_note_reblog', function ($actor, $note) {
+Events::on('on_note_reblog', function ($actor, $note): void {
     if ($episodeId = $note->episode_id) {
         model('EpisodeModel')
             ->where('id', $episodeId)
@@ -125,7 +125,7 @@ Events::on('on_note_reblog', function ($actor, $note) {
     }
 });
 
-Events::on('on_note_undo_reblog', function ($reblogNote) {
+Events::on('on_note_undo_reblog', function ($reblogNote): void {
     $note = $reblogNote->reblog_of_note;
     if ($episodeId = $note->episode_id) {
         model('EpisodeModel')
@@ -147,21 +147,21 @@ Events::on('on_note_undo_reblog', function ($reblogNote) {
     }
 });
 
-Events::on('on_note_reply', function ($reply) {
+Events::on('on_note_reply', function ($reply): void {
     $note = $reply->reply_to_note;
 
     cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
     cache()->deleteMatching("page_note#{$note->id}*");
 });
 
-Events::on('on_reply_remove', function ($reply) {
+Events::on('on_reply_remove', function ($reply): void {
     $note = $reply->reply_to_note;
 
     cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
     cache()->deleteMatching("page_note#{$note->id}*");
 });
 
-Events::on('on_note_favourite', function ($actor, $note) {
+Events::on('on_note_favourite', function ($actor, $note): void {
     if ($note->episode_id) {
         model('EpisodeModel')
             ->where('id', $note->episode_id)
@@ -176,7 +176,7 @@ Events::on('on_note_favourite', function ($actor, $note) {
     }
 });
 
-Events::on('on_note_undo_favourite', function ($actor, $note) {
+Events::on('on_note_undo_favourite', function ($actor, $note): void {
     if ($note->episode_id) {
         model('EpisodeModel')
             ->where('id', $note->episode_id)
@@ -191,22 +191,22 @@ Events::on('on_note_undo_favourite', function ($actor, $note) {
     }
 });
 
-Events::on('on_block_actor', function ($actorId) {
+Events::on('on_block_actor', function ($actorId): void {
     cache()->deleteMatching('page_podcast*');
     cache()->deleteMatching('page_note*');
 });
 
-Events::on('on_unblock_actor', function ($actorId) {
+Events::on('on_unblock_actor', function ($actorId): void {
     cache()->deleteMatching('page_podcast*');
     cache()->deleteMatching('page_note*');
 });
 
-Events::on('on_block_domain', function ($domainName) {
+Events::on('on_block_domain', function ($domainName): void {
     cache()->deleteMatching('page_podcast*');
     cache()->deleteMatching('page_note*');
 });
 
-Events::on('on_unblock_domain', function ($domainName) {
+Events::on('on_unblock_domain', function ($domainName): void {
     cache()->deleteMatching('page_podcast*');
     cache()->deleteMatching('page_note*');
 });
diff --git a/app/Config/Filters.php b/app/Config/Filters.php
index 45a9ac6153..6a70932800 100644
--- a/app/Config/Filters.php
+++ b/app/Config/Filters.php
@@ -2,6 +2,10 @@
 
 namespace Config;
 
+use Myth\Auth\Filters\LoginFilter;
+use Myth\Auth\Filters\RoleFilter;
+use App\Filters\PermissionFilter;
+use ActivityPub\Filters\ActivityPubFilter;
 use CodeIgniter\Config\BaseConfig;
 use CodeIgniter\Filters\CSRF;
 use CodeIgniter\Filters\DebugToolbar;
@@ -19,10 +23,10 @@ class Filters extends BaseConfig
         'csrf' => CSRF::class,
         'toolbar' => DebugToolbar::class,
         'honeypot' => Honeypot::class,
-        'login' => \Myth\Auth\Filters\LoginFilter::class,
-        'role' => \Myth\Auth\Filters\RoleFilter::class,
-        'permission' => \App\Filters\PermissionFilter::class,
-        'activity-pub' => \ActivityPub\Filters\ActivityPubFilter::class,
+        'login' => LoginFilter::class,
+        'role' => RoleFilter::class,
+        'permission' => PermissionFilter::class,
+        'activity-pub' => ActivityPubFilter::class,
     ];
 
     /**
diff --git a/app/Config/Format.php b/app/Config/Format.php
index e772f6ad00..b3ba469041 100644
--- a/app/Config/Format.php
+++ b/app/Config/Format.php
@@ -2,6 +2,8 @@
 
 namespace Config;
 
+use CodeIgniter\Format\JSONFormatter;
+use CodeIgniter\Format\XMLFormatter;
 use CodeIgniter\Config\BaseConfig;
 use CodeIgniter\Format\FormatterInterface;
 
@@ -40,9 +42,9 @@ class Format extends BaseConfig
      * @var array<string, string>
      */
     public $formatters = [
-        'application/json' => 'CodeIgniter\Format\JSONFormatter',
-        'application/xml' => 'CodeIgniter\Format\XMLFormatter',
-        'text/xml' => 'CodeIgniter\Format\XMLFormatter',
+        'application/json' => JSONFormatter::class,
+        'application/xml' => XMLFormatter::class,
+        'text/xml' => XMLFormatter::class,
     ];
 
     /**
@@ -62,17 +64,12 @@ class Format extends BaseConfig
     ];
 
     //--------------------------------------------------------------------
-
     /**
      * A Factory method to return the appropriate formatter for the given mime type.
      *
-     * @param string $mime
-     *
-     * @return FormatterInterface
-     *
      * @deprecated This is an alias of `\CodeIgniter\Format\Format::getFormatter`. Use that instead.
      */
-    public function getFormatter(string $mime)
+    public function getFormatter(string $mime): FormatterInterface
     {
         return Services::format()->getFormatter($mime);
     }
diff --git a/app/Config/Kint.php b/app/Config/Kint.php
index 0a4301c9c7..8d5ed9e0ce 100644
--- a/app/Config/Kint.php
+++ b/app/Config/Kint.php
@@ -23,39 +23,70 @@ class Kint extends BaseConfig
 	|--------------------------------------------------------------------------
 	*/
 
-    public $plugins = null;
+    public $plugins;
 
+    /**
+     * @var int
+     */
     public $maxDepth = 6;
 
+    /**
+     * @var bool
+     */
     public $displayCalledFrom = true;
 
+    /**
+     * @var bool
+     */
     public $expanded = false;
 
     /*
-	|--------------------------------------------------------------------------
-	| RichRenderer Settings
-	|--------------------------------------------------------------------------
-	*/
+    |--------------------------------------------------------------------------
+    | RichRenderer Settings
+    |--------------------------------------------------------------------------
+    */
+    /**
+     * @var string
+     */
     public $richTheme = 'aante-light.css';
 
+    /**
+     * @var bool
+     */
     public $richFolder = false;
 
+    /**
+     * @var int
+     */
     public $richSort = Renderer::SORT_FULL;
 
-    public $richObjectPlugins = null;
+    public $richObjectPlugins;
 
-    public $richTabPlugins = null;
+    public $richTabPlugins;
 
     /*
-	|--------------------------------------------------------------------------
-	| CLI Settings
-	|--------------------------------------------------------------------------
-	*/
+    |--------------------------------------------------------------------------
+    | CLI Settings
+    |--------------------------------------------------------------------------
+    */
+
+    /**
+     * @var bool
+     */
     public $cliColors = true;
 
+    /**
+     * @var bool
+     */
     public $cliForceUTF8 = false;
 
+    /**
+     * @var bool
+     */
     public $cliDetectWidth = true;
 
+    /**
+     * @var int
+     */
     public $cliMinWidth = 40;
 }
diff --git a/app/Config/Logger.php b/app/Config/Logger.php
index a1d60a1d0b..5607dcfb32 100644
--- a/app/Config/Logger.php
+++ b/app/Config/Logger.php
@@ -2,6 +2,7 @@
 
 namespace Config;
 
+use CodeIgniter\Log\Handlers\FileHandler;
 use CodeIgniter\Config\BaseConfig;
 
 class Logger extends BaseConfig
@@ -82,7 +83,7 @@ class Logger extends BaseConfig
          * File Handler
          * --------------------------------------------------------------------
          */
-        'CodeIgniter\Log\Handlers\FileHandler' => [
+        FileHandler::class => [
             /*
              * The log levels that this handler will handle.
              */
diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php
index 574db6d829..39c2aacece 100644
--- a/app/Config/Mimes.php
+++ b/app/Config/Mimes.php
@@ -22,7 +22,7 @@ class Mimes
     /**
      * Map of extensions to mime types.
      *
-     * @var array
+     * @var array<string, string>
      */
     public static $mimes = [
         'hqx' => [
@@ -321,11 +321,9 @@ class Mimes
     /**
      * Attempts to determine the best mime type for the given file extension.
      *
-     * @param string $extension
-     *
      * @return string|null The mime type found, or none if unable to determine.
      */
-    public static function guessTypeFromExtension(string $extension)
+    public static function guessTypeFromExtension(string $extension): ?string
     {
         $extension = trim(strtolower($extension), '. ');
 
@@ -341,15 +339,13 @@ class Mimes
     /**
      * Attempts to determine the best file extension for a given mime type.
      *
-     * @param string      $type
      * @param string|null $proposedExtension - default extension (in case there is more than one with the same mime type)
-     *
      * @return string|null The extension determined, or null if unable to match.
      */
     public static function guessExtensionFromType(
         string $type,
         string $proposedExtension = null
-    ) {
+    ): ?string {
         $type = trim(strtolower($type), '. ');
 
         $proposedExtension = trim(strtolower($proposedExtension));
diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 3c0064e318..2bc5d9bcd8 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -54,7 +54,7 @@ $routes->addPlaceholder(
 $routes->get('/', 'Home::index', ['as' => 'home']);
 
 // Install Wizard route
-$routes->group(config('App')->installGateway, function ($routes) {
+$routes->group(config('App')->installGateway, function ($routes): void {
     $routes->get('/', 'Install', ['as' => 'install']);
     $routes->post('instance-config', 'Install::attemptInstanceConfig', [
         'as' => 'instance-config',
@@ -76,12 +76,12 @@ $routes->get('.well-known/platforms', 'Platform');
 $routes->group(
     config('App')->adminGateway,
     ['namespace' => 'App\Controllers\Admin'],
-    function ($routes) {
+    function ($routes): void {
         $routes->get('/', 'Home', [
             'as' => 'admin',
         ]);
 
-        $routes->group('persons', function ($routes) {
+        $routes->group('persons', function ($routes): void {
             $routes->get('/', 'Person', [
                 'as' => 'person-list',
                 'filter' => 'permission:person-list',
@@ -93,7 +93,7 @@ $routes->group(
             $routes->post('new', 'Person::attemptCreate', [
                 'filter' => 'permission:person-create',
             ]);
-            $routes->group('(:num)', function ($routes) {
+            $routes->group('(:num)', function ($routes): void {
                 $routes->get('/', 'Person::view/$1', [
                     'as' => 'person-view',
                     'filter' => 'permission:person-view',
@@ -113,7 +113,7 @@ $routes->group(
         });
 
         // Podcasts
-        $routes->group('podcasts', function ($routes) {
+        $routes->group('podcasts', function ($routes): void {
             $routes->get('/', 'Podcast::list', [
                 'as' => 'podcast-list',
             ]);
@@ -134,7 +134,7 @@ $routes->group(
 
             // Podcast
             // Use ids in admin area to help permission and group lookups
-            $routes->group('(:num)', function ($routes) {
+            $routes->group('(:num)', function ($routes): void {
                 $routes->get('/', 'Podcast::view/$1', [
                     'as' => 'podcast-view',
                     'filter' => 'permission:podcasts-view,podcast-view',
@@ -151,7 +151,7 @@ $routes->group(
                     'filter' => 'permission:podcasts-delete',
                 ]);
 
-                $routes->group('persons', function ($routes) {
+                $routes->group('persons', function ($routes): void {
                     $routes->get('/', 'PodcastPerson/$1', [
                         'as' => 'podcast-person-manage',
                         'filter' => 'permission:podcast-edit',
@@ -170,7 +170,7 @@ $routes->group(
                     );
                 });
 
-                $routes->group('analytics', function ($routes) {
+                $routes->group('analytics', function ($routes): void {
                     $routes->get('/', 'Podcast::viewAnalytics/$1', [
                         'as' => 'podcast-analytics',
                         'filter' => 'permission:podcasts-view,podcast-view',
@@ -226,7 +226,7 @@ $routes->group(
                 });
 
                 // Podcast episodes
-                $routes->group('episodes', function ($routes) {
+                $routes->group('episodes', function ($routes): void {
                     $routes->get('/', 'Episode::list/$1', [
                         'as' => 'episode-list',
                         'filter' =>
@@ -241,7 +241,7 @@ $routes->group(
                     ]);
 
                     // Episode
-                    $routes->group('(:num)', function ($routes) {
+                    $routes->group('(:num)', function ($routes): void {
                         $routes->get('/', 'Episode::view/$1/$2', [
                             'as' => 'episode-view',
                             'filter' =>
@@ -349,7 +349,7 @@ $routes->group(
                             ],
                         );
 
-                        $routes->group('persons', function ($routes) {
+                        $routes->group('persons', function ($routes): void {
                             $routes->get('/', 'EpisodePerson/$1/$2', [
                                 'as' => 'episode-person-manage',
                                 'filter' => 'permission:podcast_episodes-edit',
@@ -376,7 +376,7 @@ $routes->group(
                 });
 
                 // Podcast contributors
-                $routes->group('contributors', function ($routes) {
+                $routes->group('contributors', function ($routes): void {
                     $routes->get('/', 'Contributor::list/$1', [
                         'as' => 'contributor-list',
                         'filter' =>
@@ -391,7 +391,7 @@ $routes->group(
                     ]);
 
                     // Contributor
-                    $routes->group('(:num)', function ($routes) {
+                    $routes->group('(:num)', function ($routes): void {
                         $routes->get('/', 'Contributor::view/$1/$2', [
                             'as' => 'contributor-view',
                             'filter' =>
@@ -418,7 +418,7 @@ $routes->group(
                     });
                 });
 
-                $routes->group('platforms', function ($routes) {
+                $routes->group('platforms', function ($routes): void {
                     $routes->get(
                         '/',
                         'PodcastPlatform::platforms/$1/podcasting',
@@ -464,7 +464,7 @@ $routes->group(
         });
 
         // Instance wide Fediverse config
-        $routes->group('fediverse', function ($routes) {
+        $routes->group('fediverse', function ($routes): void {
             $routes->get('/', 'Fediverse::dashboard', [
                 'as' => 'fediverse-dashboard',
             ]);
@@ -479,7 +479,7 @@ $routes->group(
         });
 
         // Pages
-        $routes->group('pages', function ($routes) {
+        $routes->group('pages', function ($routes): void {
             $routes->get('/', 'Page::list', ['as' => 'page-list']);
             $routes->get('new', 'Page::create', [
                 'as' => 'page-create',
@@ -489,7 +489,7 @@ $routes->group(
                 'filter' => 'permission:pages-manage',
             ]);
 
-            $routes->group('(:num)', function ($routes) {
+            $routes->group('(:num)', function ($routes): void {
                 $routes->get('/', 'Page::view/$1', ['as' => 'page-view']);
                 $routes->get('edit', 'Page::edit/$1', [
                     'as' => 'page-edit',
@@ -507,7 +507,7 @@ $routes->group(
         });
 
         // Users
-        $routes->group('users', function ($routes) {
+        $routes->group('users', function ($routes): void {
             $routes->get('/', 'User::list', [
                 'as' => 'user-list',
                 'filter' => 'permission:users-list',
@@ -521,7 +521,7 @@ $routes->group(
             ]);
 
             // User
-            $routes->group('(:num)', function ($routes) {
+            $routes->group('(:num)', function ($routes): void {
                 $routes->get('/', 'User::view/$1', [
                     'as' => 'user-view',
                     'filter' => 'permission:users-view',
@@ -553,7 +553,7 @@ $routes->group(
         });
 
         // My account
-        $routes->group('my-account', function ($routes) {
+        $routes->group('my-account', function ($routes): void {
             $routes->get('/', 'MyAccount', [
                 'as' => 'my-account',
             ]);
@@ -568,7 +568,7 @@ $routes->group(
 /**
  * Overwriting Myth:auth routes file
  */
-$routes->group(config('App')->authGateway, function ($routes) {
+$routes->group(config('App')->authGateway, function ($routes): void {
     // Login/out
     $routes->get('login', 'Auth::login', ['as' => 'login']);
     $routes->post('login', 'Auth::attemptLogin');
@@ -600,7 +600,7 @@ $routes->group(config('App')->authGateway, function ($routes) {
 });
 
 // Podcast's Public routes
-$routes->group('@(:podcastName)', function ($routes) {
+$routes->group('@(:podcastName)', function ($routes): void {
     $routes->get('/', 'Podcast::activity/$1', [
         'as' => 'podcast-activity',
     ]);
@@ -621,7 +621,7 @@ $routes->group('@(:podcastName)', function ($routes) {
     $routes->get('episodes', 'Podcast::episodes/$1', [
         'as' => 'podcast-episodes',
     ]);
-    $routes->group('episodes/(:slug)', function ($routes) {
+    $routes->group('episodes/(:slug)', function ($routes): void {
         $routes->get('/', 'Episode/$1/$2', [
             'as' => 'episode',
         ]);
@@ -631,7 +631,7 @@ $routes->group('@(:podcastName)', function ($routes) {
         $routes->get('oembed.xml', 'Episode::oembedXML/$1/$2', [
             'as' => 'episode-oembed-xml',
         ]);
-        $routes->group('embeddable-player', function ($routes) {
+        $routes->group('embeddable-player', function ($routes): void {
             $routes->get('/', 'Episode::embeddablePlayer/$1/$2', [
                 'as' => 'embeddable-player',
             ]);
@@ -661,13 +661,13 @@ $routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [
 /**
  * Overwriting ActivityPub routes file
  */
-$routes->group('@(:podcastName)', function ($routes) {
+$routes->group('@(:podcastName)', function ($routes): void {
     $routes->post('notes/new', 'Note::attemptCreate/$1', [
         'as' => 'note-attempt-create',
         'filter' => 'permission:podcast-manage_publications',
     ]);
     // Note
-    $routes->group('notes/(:uuid)', function ($routes) {
+    $routes->group('notes/(:uuid)', function ($routes): void {
         $routes->get('/', 'Note/$1/$2', [
             'as' => 'note',
             'alternate-content' => [
diff --git a/app/Controllers/Actor.php b/app/Controllers/Actor.php
index a063e2b370..16bd68cad8 100644
--- a/app/Controllers/Actor.php
+++ b/app/Controllers/Actor.php
@@ -23,7 +23,7 @@ class Actor extends \ActivityPub\Controllers\ActorController
             $this->registerPodcastWebpageHit($this->actor->podcast->id);
         }
 
-        $cacheName = "page_podcast@{$this->actor->username}_follow";
+        $cacheName = "page_podcast-{$this->actor->username}_follow";
         if (!($cachedView = cache($cacheName))) {
             helper(['form', 'components', 'svg']);
             $data = [
diff --git a/app/Controllers/Admin/BaseController.php b/app/Controllers/Admin/BaseController.php
index a349ac6f5c..43deabd6cc 100644
--- a/app/Controllers/Admin/BaseController.php
+++ b/app/Controllers/Admin/BaseController.php
@@ -31,16 +31,12 @@ class BaseController extends Controller
 
     /**
      * Constructor.
-     *
-     * @param RequestInterface  $request
-     * @param ResponseInterface $response
-     * @param LoggerInterface   $logger
      */
     public function initController(
         RequestInterface $request,
         ResponseInterface $response,
         LoggerInterface $logger
-    ) {
+    ): void {
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
 
diff --git a/app/Controllers/Admin/Contributor.php b/app/Controllers/Admin/Contributor.php
index 01e66ee695..9ae1e21bf3 100644
--- a/app/Controllers/Admin/Contributor.php
+++ b/app/Controllers/Admin/Contributor.php
@@ -8,6 +8,10 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast;
+use App\Entities\User;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use Exception;
 use App\Authorization\GroupModel;
 use App\Models\PodcastModel;
 use App\Models\UserModel;
@@ -15,12 +19,12 @@ use App\Models\UserModel;
 class Contributor extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     /**
-     * @var \App\Entities\User|null
+     * @var User|null
      */
     protected $user;
 
@@ -28,18 +32,20 @@ class Contributor extends BaseController
     {
         $this->podcast = (new PodcastModel())->getPodcastById($params[0]);
 
-        if (count($params) > 1) {
-            if (
-                !($this->user = (new UserModel())->getPodcastContributor(
-                    $params[1],
-                    $params[0]
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) <= 1) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        if (
+            $this->user = (new UserModel())->getPodcastContributor(
+                $params[1],
+                $params[0],
+            )
+        ) {
+            return $this->$method();
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function list()
@@ -57,7 +63,7 @@ class Contributor extends BaseController
         $data = [
             'contributor' => (new UserModel())->getPodcastContributor(
                 $this->user->id,
-                $this->podcast->id
+                $this->podcast->id,
             ),
         ];
 
@@ -79,7 +85,7 @@ class Contributor extends BaseController
                 $result[$user->id] = $user->username;
                 return $result;
             },
-            []
+            [],
         );
 
         $roles = (new GroupModel())->getContributorRoles();
@@ -89,7 +95,7 @@ class Contributor extends BaseController
                 $result[$role->id] = lang('Contributor.roles.' . $role->name);
                 return $result;
             },
-            []
+            [],
         );
 
         $data = [
@@ -108,9 +114,9 @@ class Contributor extends BaseController
             (new PodcastModel())->addPodcastContributor(
                 $this->request->getPost('user'),
                 $this->podcast->id,
-                $this->request->getPost('role')
+                $this->request->getPost('role'),
             );
-        } catch (\Exception $e) {
+        } catch (Exception $exception) {
             return redirect()
                 ->back()
                 ->withInput()
@@ -133,7 +139,7 @@ class Contributor extends BaseController
                 $result[$role->id] = lang('Contributor.roles.' . $role->name);
                 return $result;
             },
-            []
+            [],
         );
 
         $data = [
@@ -141,7 +147,7 @@ class Contributor extends BaseController
             'user' => $this->user,
             'contributorGroupId' => (new PodcastModel())->getContributorGroupId(
                 $this->user->id,
-                $this->podcast->id
+                $this->podcast->id,
             ),
             'roleOptions' => $roleOptions,
         ];
@@ -158,7 +164,7 @@ class Contributor extends BaseController
         (new PodcastModel())->updatePodcastContributor(
             $this->user->id,
             $this->podcast->id,
-            $this->request->getPost('role')
+            $this->request->getPost('role'),
         );
 
         return redirect()->route('contributor-list', [$this->podcast->id]);
@@ -178,7 +184,7 @@ class Contributor extends BaseController
         if (
             !$podcastModel->removePodcastContributor(
                 $this->user->id,
-                $this->podcast->id
+                $this->podcast->id,
             )
         ) {
             return redirect()
@@ -193,7 +199,7 @@ class Contributor extends BaseController
                 lang('Contributor.messages.removeContributorSuccess', [
                     'username' => $this->user->username,
                     'podcastTitle' => $this->podcast->title,
-                ])
+                ]),
             );
     }
 }
diff --git a/app/Controllers/Admin/Episode.php b/app/Controllers/Admin/Episode.php
index 619f420ef0..bbd3318d28 100644
--- a/app/Controllers/Admin/Episode.php
+++ b/app/Controllers/Admin/Episode.php
@@ -8,6 +8,7 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Episode as EpisodeEntity;
 use App\Entities\Note;
 use App\Models\EpisodeModel;
 use App\Models\NoteModel;
@@ -18,17 +19,17 @@ use CodeIgniter\I18n\Time;
 class Episode extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     /**
-     * @var \App\Entities\Episode|null
+     * @var Episode|null
      */
     protected $episode;
 
     /**
-     * @var \App\Entities\Soundbite|null
+     * @var Soundbite|null
      */
     protected $soundbites;
 
@@ -123,7 +124,7 @@ class Episode extends BaseController
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $newEpisode = new \App\Entities\Episode([
+        $newEpisode = new EpisodeEntity([
             'podcast_id' => $this->podcast->id,
             'title' => $this->request->getPost('title'),
             'slug' => $this->request->getPost('slug'),
diff --git a/app/Controllers/Admin/EpisodePerson.php b/app/Controllers/Admin/EpisodePerson.php
index e6ccca6410..420efb8fe6 100644
--- a/app/Controllers/Admin/EpisodePerson.php
+++ b/app/Controllers/Admin/EpisodePerson.php
@@ -8,6 +8,9 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast;
+use App\Entities\Episode;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\EpisodePersonModel;
 use App\Models\PodcastModel;
 use App\Models\EpisodeModel;
@@ -16,42 +19,39 @@ use App\Models\PersonModel;
 class EpisodePerson extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     /**
-     * @var \App\Entities\Episode
+     * @var Episode
      */
     protected $episode;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 1) {
-            if (
-                !($this->podcast = (new PodcastModel())->getPodcastById(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
-            if (
-                !($this->episode = (new EpisodeModel())
-                    ->where([
-                        'id' => $params[1],
-                        'podcast_id' => $params[0],
-                    ])
-                    ->first())
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
-        } else {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+        if (count($params) <= 2) {
+            throw PageNotFoundException::forPageNotFound();
         }
-        unset($params[1]);
-        unset($params[0]);
 
-        return $this->$method(...$params);
+        if (
+            ($this->podcast = (new PodcastModel())->getPodcastById(
+                $params[0],
+            )) &&
+            ($this->episode = (new EpisodeModel())
+                ->where([
+                    'id' => $params[1],
+                    'podcast_id' => $params[0],
+                ])
+                ->first())
+        ) {
+            unset($params[1]);
+            unset($params[0]);
+
+            return $this->$method(...$params);
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
diff --git a/app/Controllers/Admin/Fediverse.php b/app/Controllers/Admin/Fediverse.php
index 184a52ff26..750845756c 100644
--- a/app/Controllers/Admin/Fediverse.php
+++ b/app/Controllers/Admin/Fediverse.php
@@ -8,8 +8,6 @@
 
 namespace App\Controllers\Admin;
 
-use ActivityPub\Models\BlockedDomainModel;
-
 class Fediverse extends BaseController
 {
     public function dashboard()
diff --git a/app/Controllers/Admin/Page.php b/app/Controllers/Admin/Page.php
index f2ce56db4b..d62855d522 100644
--- a/app/Controllers/Admin/Page.php
+++ b/app/Controllers/Admin/Page.php
@@ -8,24 +8,28 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Page as EntitiesPage;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\PageModel;
 
 class Page extends BaseController
 {
     /**
-     * @var \App\Entities\Page|null
+     * @var Page|null
      */
     protected $page;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (!($this->page = (new PageModel())->find($params[0]))) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        if ($this->page = (new PageModel())->find($params[0])) {
+            return $this->$method();
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     function list()
@@ -51,7 +55,7 @@ class Page extends BaseController
 
     function attemptCreate()
     {
-        $page = new \App\Entities\Page([
+        $page = new EntitiesPage([
             'title' => $this->request->getPost('title'),
             'slug' => $this->request->getPost('slug'),
             'content' => $this->request->getPost('content'),
@@ -72,7 +76,7 @@ class Page extends BaseController
                 'message',
                 lang('Page.messages.createSuccess', [
                     'pageTitle' => $page->title,
-                ])
+                ]),
             );
     }
 
diff --git a/app/Controllers/Admin/Person.php b/app/Controllers/Admin/Person.php
index f3dea00e4b..8a2f33b564 100644
--- a/app/Controllers/Admin/Person.php
+++ b/app/Controllers/Admin/Person.php
@@ -8,28 +8,28 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Person as EntitiesPerson;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\PersonModel;
 
 class Person extends BaseController
 {
     /**
-     * @var \App\Entities\Person|null
+     * @var Person|null
      */
     protected $person;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->person = (new PersonModel())->getPersonById(
-                    $params[0]
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        if ($this->person = (new PersonModel())->getPersonById($params[0])) {
+            return $this->$method();
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
@@ -68,7 +68,7 @@ class Person extends BaseController
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $person = new \App\Entities\Person([
+        $person = new EntitiesPerson([
             'full_name' => $this->request->getPost('full_name'),
             'unique_name' => $this->request->getPost('unique_name'),
             'information_url' => $this->request->getPost('information_url'),
@@ -118,14 +118,14 @@ class Person extends BaseController
         $this->person->full_name = $this->request->getPost('full_name');
         $this->person->unique_name = $this->request->getPost('unique_name');
         $this->person->information_url = $this->request->getPost(
-            'information_url'
+            'information_url',
         );
         $image = $this->request->getFile('image');
         if ($image->isValid()) {
             $this->person->image = $image;
         }
 
-        $this->updated_by = user()->id;
+        $this->person->updated_by = user()->id;
 
         $personModel = new PersonModel();
         if (!$personModel->update($this->person->id, $this->person)) {
diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php
index fcd7550006..7919ae4c37 100644
--- a/app/Controllers/Admin/Podcast.php
+++ b/app/Controllers/Admin/Podcast.php
@@ -8,6 +8,9 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast as EntitiesPodcast;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use Config\Database;
 use App\Models\CategoryModel;
 use App\Models\LanguageModel;
 use App\Models\PodcastModel;
@@ -17,23 +20,21 @@ use Config\Services;
 class Podcast extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast|null
+     * @var Podcast|null
      */
     protected $podcast;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->podcast = (new PodcastModel())->getPodcastById(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) {
+            return $this->$method();
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function list()
@@ -145,7 +146,7 @@ class Podcast extends BaseController
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $podcast = new \App\Entities\Podcast([
+        $podcast = new EntitiesPodcast([
             'title' => $this->request->getPost('title'),
             'name' => $this->request->getPost('name'),
             'description_markdown' => $this->request->getPost('description'),
@@ -175,7 +176,7 @@ class Podcast extends BaseController
         ]);
 
         $podcastModel = new PodcastModel();
-        $db = \Config\Database::connect();
+        $db = Database::connect();
 
         $db->transStart();
 
@@ -271,18 +272,18 @@ class Podcast extends BaseController
         );
         $this->podcast->partner_id = $this->request->getPost('partner_id');
         $this->podcast->partner_link_url = $this->request->getPost(
-            'partner_link_url'
+            'partner_link_url',
         );
         $this->podcast->partner_image_url = $this->request->getPost(
-            'partner_image_url'
+            'partner_image_url',
         );
         $this->podcast->is_blocked = $this->request->getPost('block') === 'yes';
         $this->podcast->is_completed =
             $this->request->getPost('complete') === 'yes';
         $this->podcast->is_locked = $this->request->getPost('lock') === 'yes';
-        $this->updated_by = user()->id;
+        $this->podcast->updated_by = user()->id;
 
-        $db = \Config\Database::connect();
+        $db = Database::connect();
         $db->transStart();
 
         $podcastModel = new PodcastModel();
diff --git a/app/Controllers/Admin/PodcastImport.php b/app/Controllers/Admin/PodcastImport.php
index 6a29326156..230e6db5bc 100644
--- a/app/Controllers/Admin/PodcastImport.php
+++ b/app/Controllers/Admin/PodcastImport.php
@@ -8,6 +8,13 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use ErrorException;
+use Config\Database;
+use Podlibre\PodcastNamespace\ReversedTaxonomy;
+use App\Entities\PodcastPerson;
+use App\Entities\Episode;
 use App\Models\CategoryModel;
 use App\Models\LanguageModel;
 use App\Models\PodcastModel;
@@ -22,23 +29,21 @@ use League\HTMLToMarkdown\HtmlConverter;
 class PodcastImport extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast|null
+     * @var Podcast|null
      */
     protected $podcast;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->podcast = (new PodcastModel())->getPodcastById(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
+        }
+
+        if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
@@ -80,12 +85,12 @@ class PodcastImport extends BaseController
             $feed = simplexml_load_file(
                 $this->request->getPost('imported_feed_url'),
             );
-        } catch (\ErrorException $ex) {
+        } catch (ErrorException $errorException) {
             return redirect()
                 ->back()
                 ->withInput()
                 ->with('errors', [
-                    $ex->getMessage() .
+                    $errorException->getMessage() .
                     ': <a href="' .
                     $this->request->getPost('imported_feed_url') .
                     '" rel="noreferrer noopener" target="_blank">' .
@@ -115,7 +120,7 @@ class PodcastImport extends BaseController
         $channelDescriptionHtml = (string) $feed->channel[0]->description;
 
         try {
-            $podcast = new \App\Entities\Podcast([
+            $podcast = new Podcast([
                 'name' => $this->request->getPost('name'),
                 'imported_feed_url' => $this->request->getPost(
                     'imported_feed_url',
@@ -157,9 +162,9 @@ class PodcastImport extends BaseController
                 'is_completed' => empty($nsItunes->complete)
                     ? false
                     : $nsItunes->complete === 'yes',
-                'location_name' => !$nsPodcast->location
-                    ? null
-                    : (string) $nsPodcast->location,
+                'location_name' => $nsPodcast->location
+                    ? (string) $nsPodcast->location
+                    : null,
                 'location_geo' =>
                     !$nsPodcast->location ||
                     empty($nsPodcast->location->attributes()['geo'])
@@ -173,7 +178,7 @@ class PodcastImport extends BaseController
                 'created_by' => user()->id,
                 'updated_by' => user()->id,
             ]);
-        } catch (\ErrorException $ex) {
+        } catch (ErrorException $ex) {
             return redirect()
                 ->back()
                 ->withInput()
@@ -188,7 +193,7 @@ class PodcastImport extends BaseController
         }
 
         $podcastModel = new PodcastModel();
-        $db = \Config\Database::connect();
+        $db = Database::connect();
 
         $db->transStart();
 
@@ -221,13 +226,13 @@ class PodcastImport extends BaseController
                 $platformLabel = $platform->attributes()['platform'];
                 $platformSlug = slugify($platformLabel);
                 if ($platformModel->getPlatform($platformSlug)) {
-                    array_push($podcastsPlatformsData, [
+                    $podcastsPlatformsData[] = [
                         'platform_slug' => $platformSlug,
                         'podcast_id' => $newPodcastId,
                         'link_url' => $platform->attributes()['url'],
                         'link_content' => $platform->attributes()['id'],
                         'is_visible' => false,
-                    ]);
+                    ];
                 }
             }
         }
@@ -243,24 +248,22 @@ class PodcastImport extends BaseController
             $newPersonId = null;
             if ($newPerson = $personModel->getPerson($podcastPerson)) {
                 $newPersonId = $newPerson->id;
-            } else {
-                if (
-                    !($newPersonId = $personModel->createPerson(
-                        $podcastPerson,
-                        $podcastPerson->attributes()['href'],
-                        $podcastPerson->attributes()['img'],
-                    ))
-                ) {
-                    return redirect()
-                        ->back()
-                        ->withInput()
-                        ->with('errors', $personModel->errors());
-                }
+            } elseif (
+                !($newPersonId = $personModel->createPerson(
+                    $podcastPerson,
+                    $podcastPerson->attributes()['href'],
+                    $podcastPerson->attributes()['img'],
+                ))
+            ) {
+                return redirect()
+                    ->back()
+                    ->withInput()
+                    ->with('errors', $personModel->errors());
             }
 
             $personGroup = empty($podcastPerson->attributes()['group'])
                 ? ['slug' => '']
-                : \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[
+                : ReversedTaxonomy::$taxonomy[
                     (string) $podcastPerson->attributes()['group']
                 ];
             $personRole =
@@ -270,7 +273,7 @@ class PodcastImport extends BaseController
                     : $personGroup['roles'][
                         strval($podcastPerson->attributes()['role'])
                     ];
-            $newPodcastPerson = new \App\Entities\PodcastPerson([
+            $newPodcastPerson = new PodcastPerson([
                 'podcast_id' => $newPodcastId,
                 'person_id' => $newPersonId,
                 'person_group' => $personGroup['slug'],
@@ -297,7 +300,7 @@ class PodcastImport extends BaseController
 
         //////////////////////////////////////////////////////////////////
         // For each Episode:
-        for ($itemNumber = 1; $itemNumber <= $lastItem; $itemNumber++) {
+        for ($itemNumber = 1; $itemNumber <= $lastItem; ++$itemNumber) {
             $item = $feed->channel[0]->item[$numberItems - $itemNumber];
 
             $nsItunes = $item->children(
@@ -318,7 +321,7 @@ class PodcastImport extends BaseController
             if (in_array($slug, $slugs)) {
                 $slugNumber = 2;
                 while (in_array($slug . '-' . $slugNumber, $slugs)) {
-                    $slugNumber++;
+                    ++$slugNumber;
                 }
                 $slug = $slug . '-' . $slugNumber;
             }
@@ -340,7 +343,7 @@ class PodcastImport extends BaseController
                     $itemDescriptionHtml = $item->description;
             }
 
-            $newEpisode = new \App\Entities\Episode([
+            $newEpisode = new Episode([
                 'podcast_id' => $newPodcastId,
                 'guid' => empty($item->guid) ? null : $item->guid,
                 'title' => $item->title,
@@ -366,15 +369,15 @@ class PodcastImport extends BaseController
                 'number' =>
                     $this->request->getPost('force_renumber') === 'yes'
                         ? $itemNumber
-                        : (!empty($nsItunes->episode)
-                            ? $nsItunes->episode
-                            : null),
+                        : (empty($nsItunes->episode)
+                            ? null
+                            : $nsItunes->episode),
                 'season_number' => empty(
                     $this->request->getPost('season_number')
                 )
-                    ? (!empty($nsItunes->season)
-                        ? $nsItunes->season
-                        : null)
+                    ? (empty($nsItunes->season)
+                        ? null
+                        : $nsItunes->season)
                     : $this->request->getPost('season_number'),
                 'type' => empty($nsItunes->episodeType)
                     ? 'full'
@@ -382,9 +385,9 @@ class PodcastImport extends BaseController
                 'is_blocked' => empty($nsItunes->block)
                     ? false
                     : $nsItunes->block === 'yes',
-                'location_name' => !$nsPodcast->location
-                    ? null
-                    : $nsPodcast->location,
+                'location_name' => $nsPodcast->location
+                    ? $nsPodcast->location
+                    : null,
                 'location_geo' =>
                     !$nsPodcast->location ||
                     empty($nsPodcast->location->attributes()['geo'])
@@ -415,24 +418,22 @@ class PodcastImport extends BaseController
                 $newPersonId = null;
                 if ($newPerson = $personModel->getPerson($episodePerson)) {
                     $newPersonId = $newPerson->id;
-                } else {
-                    if (
-                        !($newPersonId = $personModel->createPerson(
-                            $episodePerson,
-                            $episodePerson->attributes()['href'],
-                            $episodePerson->attributes()['img'],
-                        ))
-                    ) {
-                        return redirect()
-                            ->back()
-                            ->withInput()
-                            ->with('errors', $personModel->errors());
-                    }
+                } elseif (
+                    !($newPersonId = $personModel->createPerson(
+                        $episodePerson,
+                        $episodePerson->attributes()['href'],
+                        $episodePerson->attributes()['img'],
+                    ))
+                ) {
+                    return redirect()
+                        ->back()
+                        ->withInput()
+                        ->with('errors', $personModel->errors());
                 }
 
                 $personGroup = empty($episodePerson->attributes()['group'])
                     ? ['slug' => '']
-                    : \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[
+                    : ReversedTaxonomy::$taxonomy[
                         strval($episodePerson->attributes()['group'])
                     ];
                 $personRole =
@@ -442,7 +443,7 @@ class PodcastImport extends BaseController
                         : $personGroup['roles'][
                             strval($episodePerson->attributes()['role'])
                         ];
-                $newEpisodePerson = new \App\Entities\PodcastPerson([
+                $newEpisodePerson = new PodcastPerson([
                     'podcast_id' => $newPodcastId,
                     'episode_id' => $newEpisodeId,
                     'person_id' => $newPersonId,
diff --git a/app/Controllers/Admin/PodcastPerson.php b/app/Controllers/Admin/PodcastPerson.php
index 4da46dbd83..9a9e447434 100644
--- a/app/Controllers/Admin/PodcastPerson.php
+++ b/app/Controllers/Admin/PodcastPerson.php
@@ -8,6 +8,8 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\PodcastPersonModel;
 use App\Models\PodcastModel;
 use App\Models\PersonModel;
@@ -15,26 +17,22 @@ use App\Models\PersonModel;
 class PodcastPerson extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->podcast = (new PodcastModel())->getPodcastById(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
-        } else {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+        if (count($params) === 0) {
+            throw PageNotFoundException::forPageNotFound();
         }
-        unset($params[0]);
 
-        return $this->$method(...$params);
+        if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) {
+            unset($params[0]);
+            return $this->$method(...$params);
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
diff --git a/app/Controllers/Admin/PodcastPlatform.php b/app/Controllers/Admin/PodcastPlatform.php
index 6f5fd99fd3..55d2ab9830 100644
--- a/app/Controllers/Admin/PodcastPlatform.php
+++ b/app/Controllers/Admin/PodcastPlatform.php
@@ -8,6 +8,8 @@
 
 namespace App\Controllers\Admin;
 
+use App\Entities\Podcast;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\PlatformModel;
 use App\Models\PodcastModel;
 use Config\Services;
@@ -15,20 +17,22 @@ use Config\Services;
 class PodcastPlatform extends BaseController
 {
     /**
-     * @var \App\Entities\Podcast|null
+     * @var Podcast|null
      */
     protected $podcast;
 
     public function _remap($method, ...$params)
     {
-        if (
-            !($this->podcast = (new PodcastModel())->getPodcastById($params[0]))
-        ) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+        if (count($params) === 0) {
+            return $this->$method();
+        }
+
+        if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) {
+            unset($params[0]);
+            return $this->$method(...$params);
         }
-        unset($params[0]);
 
-        return $this->$method(...$params);
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
@@ -45,7 +49,7 @@ class PodcastPlatform extends BaseController
             'platformType' => $platformType,
             'platforms' => (new PlatformModel())->getPlatformsWithLinks(
                 $this->podcast->id,
-                $platformType
+                $platformType,
             ),
         ];
 
@@ -65,36 +69,35 @@ class PodcastPlatform extends BaseController
             as $platformSlug => $podcastPlatform
         ) {
             $podcastPlatformUrl = $podcastPlatform['url'];
-
-            if (
-                !empty($podcastPlatformUrl) &&
-                $validation->check($podcastPlatformUrl, 'validate_url')
-            ) {
-                array_push($podcastsPlatformsData, [
-                    'platform_slug' => $platformSlug,
-                    'podcast_id' => $this->podcast->id,
-                    'link_url' => $podcastPlatformUrl,
-                    'link_content' => $podcastPlatform['content'],
-                    'is_visible' => array_key_exists(
-                        'visible',
-                        $podcastPlatform
-                    )
-                        ? $podcastPlatform['visible'] == 'yes'
-                        : false,
-                    'is_on_embeddable_player' => array_key_exists(
-                        'on_embeddable_player',
-                        $podcastPlatform
-                    )
-                        ? $podcastPlatform['on_embeddable_player'] == 'yes'
-                        : false,
-                ]);
+            if (empty($podcastPlatformUrl)) {
+                continue;
+            }
+            if (!$validation->check($podcastPlatformUrl, 'validate_url')) {
+                continue;
             }
+            $podcastsPlatformsData[] = [
+                'platform_slug' => $platformSlug,
+                'podcast_id' => $this->podcast->id,
+                'link_url' => $podcastPlatformUrl,
+                'link_content' => $podcastPlatform['content'],
+                'is_visible' =>
+                    array_key_exists('visible', $podcastPlatform) &&
+                    $podcastPlatform['visible'] == 'yes',
+                'is_on_embeddable_player' =>
+                    array_key_exists(
+                        'on_embeddable_player',
+                        $podcastPlatform,
+                    ) && $podcastPlatform['on_embeddable_player'] == 'yes',
+            ];
+            return redirect()
+                ->back()
+                ->with('message', lang('Platforms.messages.updateSuccess'));
         }
 
         $platformModel->savePodcastPlatforms(
             $this->podcast->id,
             $platformType,
-            $podcastsPlatformsData
+            $podcastsPlatformsData,
         );
 
         return redirect()
@@ -106,7 +109,7 @@ class PodcastPlatform extends BaseController
     {
         (new PlatformModel())->removePodcastPlatform(
             $this->podcast->id,
-            $platformSlug
+            $platformSlug,
         );
 
         return redirect()
diff --git a/app/Controllers/Admin/User.php b/app/Controllers/Admin/User.php
index be174529ae..1b456eac65 100644
--- a/app/Controllers/Admin/User.php
+++ b/app/Controllers/Admin/User.php
@@ -8,26 +8,30 @@
 
 namespace App\Controllers\Admin;
 
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Authorization\GroupModel;
+use App\Entities\User as EntitiesUser;
 use App\Models\UserModel;
 use Config\Services;
 
 class User extends BaseController
 {
     /**
-     * @var \App\Entities\User|null
+     * @var User|null
      */
     protected $user;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (!($this->user = (new UserModel())->find($params[0]))) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        if ($this->user = (new UserModel())->find($params[0])) {
+            return $this->$method();
+        }
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function list()
@@ -67,7 +71,7 @@ class User extends BaseController
             [
                 'email' => 'required|valid_email|is_unique[users.email]',
                 'password' => 'required|strong_password',
-            ]
+            ],
         );
 
         if (!$this->validate($rules)) {
@@ -78,7 +82,7 @@ class User extends BaseController
         }
 
         // Save the user
-        $user = new \App\Entities\User($this->request->getPost());
+        $user = new EntitiesUser($this->request->getPost());
 
         // Activate user
         $user->activate();
@@ -100,7 +104,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.createSuccess', [
                     'username' => $user->username,
-                ])
+                ]),
             );
     }
 
@@ -115,7 +119,7 @@ class User extends BaseController
                 $result[$role->name] = lang('User.roles.' . $role->name);
                 return $result;
             },
-            []
+            [],
         );
 
         $data = [
@@ -141,7 +145,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.rolesEditSuccess', [
                     'username' => $this->user->username,
-                ])
+                ]),
             );
     }
 
@@ -163,7 +167,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.forcePassResetSuccess', [
                     'username' => $this->user->username,
-                ])
+                ]),
             );
     }
 
@@ -196,7 +200,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.banSuccess', [
                     'username' => $this->user->username,
-                ])
+                ]),
             );
     }
 
@@ -217,7 +221,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.unbanSuccess', [
                     'username' => $this->user->username,
-                ])
+                ]),
             );
     }
 
@@ -242,7 +246,7 @@ class User extends BaseController
                 'message',
                 lang('User.messages.deleteSuccess', [
                     'username' => $this->user->username,
-                ])
+                ]),
             );
     }
 }
diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php
index 6dc477542d..0ac069f113 100644
--- a/app/Controllers/Auth.php
+++ b/app/Controllers/Auth.php
@@ -8,9 +8,11 @@
 
 namespace App\Controllers;
 
+use Myth\Auth\Controllers\AuthController;
 use App\Entities\User;
+use CodeIgniter\HTTP\RedirectResponse;
 
-class Auth extends \Myth\Auth\Controllers\AuthController
+class Auth extends AuthController
 {
     /**
      * An array of helpers to be automatically loaded
@@ -104,10 +106,8 @@ class Auth extends \Myth\Auth\Controllers\AuthController
     /**
      * Verifies the code with the email and saves the new password,
      * if they all pass validation.
-     *
-     * @return mixed
      */
-    public function attemptReset()
+    public function attemptReset(): RedirectResponse
     {
         if ($this->config->activeResetter === false) {
             return redirect()
diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php
index df816124ae..7bfe25bca2 100644
--- a/app/Controllers/BaseController.php
+++ b/app/Controllers/BaseController.php
@@ -30,16 +30,12 @@ class BaseController extends Controller
 
     /**
      * Constructor.
-     *
-     * @param RequestInterface  $request
-     * @param ResponseInterface $response
-     * @param LoggerInterface   $logger
      */
     public function initController(
         RequestInterface $request,
         ResponseInterface $response,
         LoggerInterface $logger
-    ) {
+    ): void {
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
 
diff --git a/app/Controllers/Episode.php b/app/Controllers/Episode.php
index f0533e764b..98050652a9 100644
--- a/app/Controllers/Episode.php
+++ b/app/Controllers/Episode.php
@@ -11,6 +11,7 @@ namespace App\Controllers;
 use Analytics\AnalyticsTrait;
 use App\Models\EpisodeModel;
 use App\Models\PodcastModel;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use SimpleXMLElement;
 
 class Episode extends BaseController
@@ -18,31 +19,41 @@ class Episode extends BaseController
     use AnalyticsTrait;
 
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     /**
-     * @var \App\Entities\Episode|null
+     * @var Episode
      */
     protected $episode;
 
     public function _remap($method, ...$params)
     {
-        $this->podcast = (new PodcastModel())->getPodcastByName($params[0]);
+        if (count($params) < 2) {
+            throw PageNotFoundException::forPageNotFound();
+        }
+
+        if (
+            !($this->podcast = (new PodcastModel())->getPodcastByName(
+                $params[0],
+            ))
+        ) {
+            throw PageNotFoundException::forPageNotFound();
+        }
 
         if (
-            count($params) > 1 &&
-            !($this->episode = (new EpisodeModel())->getEpisodeBySlug(
+            $this->episode = (new EpisodeModel())->getEpisodeBySlug(
                 $this->podcast->id,
                 $params[1],
-            ))
+            )
         ) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            unset($params[1]);
+            unset($params[0]);
+            return $this->$method(...$params);
         }
-        unset($params[1]);
-        unset($params[0]);
-        return $this->$method(...$params);
+
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
diff --git a/app/Controllers/Feed.php b/app/Controllers/Feed.php
index 3225e5f289..1844f20570 100644
--- a/app/Controllers/Feed.php
+++ b/app/Controllers/Feed.php
@@ -8,32 +8,34 @@
 
 namespace App\Controllers;
 
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use Opawg\UserAgentsPhp\UserAgentsRSS;
+use Exception;
 use App\Models\EpisodeModel;
 use App\Models\PodcastModel;
 use CodeIgniter\Controller;
 
 class Feed extends Controller
 {
-    public function index($podcastName)
+    public function index($podcastName): ResponseInterface
     {
         helper('rss');
 
         $podcast = (new PodcastModel())->where('name', $podcastName)->first();
         if (!$podcast) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            throw PageNotFoundException::forPageNotFound();
         }
 
         $serviceSlug = '';
         try {
-            $service = \Opawg\UserAgentsPhp\UserAgentsRSS::find(
-                $_SERVER['HTTP_USER_AGENT'],
-            );
+            $service = UserAgentsRSS::find($_SERVER['HTTP_USER_AGENT']);
             if ($service) {
                 $serviceSlug = $service['slug'];
             }
-        } catch (\Exception $e) {
+        } catch (Exception $exception) {
             // If things go wrong the show must go on and the user must be able to download the file
-            log_message('critical', $e);
+            log_message('critical', $exception);
         }
 
         $cacheName =
diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php
index ef8b0bb00e..3e107c7923 100644
--- a/app/Controllers/Home.php
+++ b/app/Controllers/Home.php
@@ -12,6 +12,9 @@ use App\Models\PodcastModel;
 
 class Home extends BaseController
 {
+    /**
+     * @return mixed
+     */
     public function index()
     {
         $model = new PodcastModel();
diff --git a/app/Controllers/Install.php b/app/Controllers/Install.php
index c3e60160a6..3fbb2ba0a5 100644
--- a/app/Controllers/Install.php
+++ b/app/Controllers/Install.php
@@ -8,6 +8,15 @@
 
 namespace App\Controllers;
 
+use CodeIgniter\HTTP\RequestInterface;
+use CodeIgniter\HTTP\ResponseInterface;
+use Psr\Log\LoggerInterface;
+use Throwable;
+use Dotenv\Exception\ValidationException;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use CodeIgniter\Database\Exceptions\DatabaseException;
+use Config\Database;
+use App\Entities\User;
 use App\Models\UserModel;
 use CodeIgniter\Controller;
 use Config\Services;
@@ -15,16 +24,19 @@ use Dotenv\Dotenv;
 
 class Install extends Controller
 {
+    /**
+     * @var string[]
+     */
     protected $helpers = ['form', 'components', 'svg'];
 
     /**
      * Constructor.
      */
     public function initController(
-        \CodeIgniter\HTTP\RequestInterface $request,
-        \CodeIgniter\HTTP\ResponseInterface $response,
-        \Psr\Log\LoggerInterface $logger
-    ) {
+        RequestInterface $request,
+        ResponseInterface $response,
+        LoggerInterface $logger
+    ): void {
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
     }
@@ -36,14 +48,14 @@ class Install extends Controller
      * If all required actions have already been performed,
      * the install route will show a 404 page.
      */
-    public function index()
+    public function index(): string
     {
         try {
             // Check if .env is created and has all required fields
             $dotenv = Dotenv::createUnsafeImmutable(ROOTPATH);
 
             $dotenv->load();
-        } catch (\Throwable $e) {
+        } catch (Throwable $e) {
             $this->createEnv();
         }
 
@@ -55,7 +67,7 @@ class Install extends Controller
                     'app.adminGateway',
                     'app.authGateway',
                 ]);
-            } catch (\Dotenv\Exception\ValidationException $e) {
+            } catch (ValidationException $e) {
                 // form to input instance configuration
                 return $this->instanceConfig();
             }
@@ -68,13 +80,13 @@ class Install extends Controller
                     'database.default.password',
                     'database.default.DBPrefix',
                 ]);
-            } catch (\Dotenv\Exception\ValidationException $e) {
+            } catch (ValidationException $validationException) {
                 return $this->databaseConfig();
             }
 
             try {
                 $dotenv->required('cache.handler');
-            } catch (\Dotenv\Exception\ValidationException $e) {
+            } catch (ValidationException $validationException) {
                 return $this->cacheConfig();
             }
         } else {
@@ -90,7 +102,7 @@ class Install extends Controller
                     'database.default.DBPrefix',
                     'cache.handler',
                 ]);
-            } catch (\Dotenv\Exception\ValidationException $e) {
+            } catch (ValidationException $e) {
                 return view('install/manual_config');
             }
         }
@@ -104,9 +116,9 @@ class Install extends Controller
                 (new UserModel())->countAll() > 0
             ) {
                 // if so, show a 404 page
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+                throw PageNotFoundException::forPageNotFound();
             }
-        } catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
+        } catch (DatabaseException $databaseException) {
             // Could not connect to the database
             // show database config view to fix value
             session()->setFlashdata(
@@ -128,6 +140,7 @@ class Install extends Controller
 
     /**
      * Returns the form to generate the .env config file for the instance.
+     * @return mixed|void
      */
     public function createEnv()
     {
@@ -135,7 +148,7 @@ class Install extends Controller
         try {
             $envFile = fopen(ROOTPATH . '.env', 'w');
             fclose($envFile);
-        } catch (\Throwable $e) {
+        } catch (Throwable $throwable) {
             // Could not create the .env file, redirect to a view with manual instructions on how to add it
             return view('install/manual_config');
         }
@@ -252,22 +265,22 @@ class Install extends Controller
     /**
      * Runs all database migrations required for instance.
      */
-    public function migrate()
+    public function migrate(): void
     {
-        $migrations = \Config\Services::migrations();
+        $migrations = Services::migrations();
 
-        !$migrations->setNamespace('Myth\Auth')->latest();
-        !$migrations->setNamespace('ActivityPub')->latest();
-        !$migrations->setNamespace('Analytics')->latest();
-        !$migrations->setNamespace(APP_NAMESPACE)->latest();
+        $migrations->setNamespace('Myth\Auth')->latest();
+        $migrations->setNamespace('ActivityPub')->latest();
+        $migrations->setNamespace('Analytics')->latest();
+        $migrations->setNamespace(APP_NAMESPACE)->latest();
     }
 
     /**
      * Runs all database seeds required for instance.
      */
-    public function seed()
+    public function seed(): void
     {
-        $seeder = \Config\Database::seeder();
+        $seeder = Database::seeder();
 
         // Seed database
         $seeder->call('AppSeeder');
@@ -308,12 +321,12 @@ class Install extends Controller
         }
 
         // Save the user
-        $user = new \App\Entities\User($this->request->getPost());
+        $user = new User($this->request->getPost());
 
         // Activate user
         $user->activate();
 
-        $db = \Config\Database::connect();
+        $db = Database::connect();
 
         $db->transStart();
         if (!($userId = $userModel->insert($user, true))) {
@@ -345,10 +358,8 @@ class Install extends Controller
      * overwrites any existing key and appends new ones
      *
      * @param array $data key/value config pairs
-     *
-     * @return void
      */
-    public static function writeEnv($configData)
+    public static function writeEnv($configData): void
     {
         $envData = file(ROOTPATH . '.env'); // reads an array of lines
 
@@ -360,7 +371,7 @@ class Install extends Controller
                 $keyVal,
                 &$replaced
             ) {
-                if (strpos($line, $key) === 0) {
+                if (strpos($line, (string) $key) === 0) {
                     $replaced = true;
                     return $keyVal;
                 }
@@ -369,7 +380,7 @@ class Install extends Controller
             $envData);
 
             if (!$replaced) {
-                array_push($envData, $keyVal);
+                $envData[] = $keyVal;
             }
         }
 
diff --git a/app/Controllers/Note.php b/app/Controllers/Note.php
index 509c78746a..6c0ff328dd 100644
--- a/app/Controllers/Note.php
+++ b/app/Controllers/Note.php
@@ -8,18 +8,22 @@
 
 namespace App\Controllers;
 
+use ActivityPub\Controllers\NoteController;
+use ActivityPub\Entities\Note as ActivityPubNote;
 use Analytics\AnalyticsTrait;
+use App\Entities\Note as CastopodNote;
 use App\Models\EpisodeModel;
 use App\Models\PodcastModel;
+use CodeIgniter\HTTP\RedirectResponse;
 use CodeIgniter\HTTP\URI;
 use CodeIgniter\I18n\Time;
 
-class Note extends \ActivityPub\Controllers\NoteController
+class Note extends NoteController
 {
     use AnalyticsTrait;
 
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
@@ -48,7 +52,7 @@ class Note extends \ActivityPub\Controllers\NoteController
         return $this->$method(...$params);
     }
 
-    public function index()
+    public function index(): RedirectResponse
     {
         // Prevent analytics hit when authenticated
         if (!can_user_interact()) {
@@ -108,7 +112,7 @@ class Note extends \ActivityPub\Controllers\NoteController
 
         $message = $this->request->getPost('message');
 
-        $newNote = new \App\Entities\Note([
+        $newNote = new CastopodNote([
             'actor_id' => interact_as_actor_id(),
             'published_at' => Time::now(),
             'created_by' => user_id(),
@@ -162,7 +166,7 @@ class Note extends \ActivityPub\Controllers\NoteController
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $newNote = new \ActivityPub\Entities\Note([
+        $newNote = new ActivityPubNote([
             'actor_id' => interact_as_actor_id(),
             'in_reply_to_id' => $this->note->id,
             'message' => $this->request->getPost('message'),
diff --git a/app/Controllers/Page.php b/app/Controllers/Page.php
index 73c452dfe9..b547a4caba 100644
--- a/app/Controllers/Page.php
+++ b/app/Controllers/Page.php
@@ -8,6 +8,8 @@
 
 namespace App\Controllers;
 
+use App\Entities\Page as PageEntity;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use App\Models\PageModel;
 use App\Models\CreditModel;
 use App\Models\PodcastModel;
@@ -15,28 +17,28 @@ use App\Models\PodcastModel;
 class Page extends BaseController
 {
     /**
-     * @var \App\Entities\Page|null
+     * @var Page|null
      */
     protected $page;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->page = (new PageModel())
-                    ->where('slug', $params[0])
-                    ->first())
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            return $this->$method();
+        }
+
+        if (
+            $this->page = (new PageModel())->where('slug', $params[0])->first()
+        ) {
+            return $this->$method();
         }
 
-        return $this->$method();
+        throw PageNotFoundException::forPageNotFound();
     }
 
     public function index()
     {
-        $cacheName = "page@{$this->page->slug}";
+        $cacheName = "page-{$this->page->slug}";
         if (!($found = cache($cacheName))) {
             $data = [
                 'page' => $this->page,
@@ -58,7 +60,7 @@ class Page extends BaseController
 
         $cacheName = "page_credits_{$locale}";
         if (!($found = cache($cacheName))) {
-            $page = new \App\Entities\Page([
+            $page = new PageEntity([
                 'title' => lang('Person.credits', [], $locale),
                 'slug' => 'credits',
                 'content' => '',
@@ -157,10 +159,10 @@ class Page extends BaseController
                         'role_label' => $credit->role_label,
                         'is_in' => [
                             [
-                                'link' => $credit->episode
+                                'link' => $credit->episode_id
                                     ? $credit->episode->link
                                     : $credit->podcast->link,
-                                'title' => $credit->episode
+                                'title' => $credit->episode_id
                                     ? (count($allPodcasts) > 1
                                             ? "{$credit->podcast->title} â–¸ "
                                             : '') .
@@ -179,10 +181,10 @@ class Page extends BaseController
                     $credits[$person_group]['persons'][$person_id]['roles'][
                         $person_role
                     ]['is_in'][] = [
-                        'link' => $credit->episode
+                        'link' => $credit->episode_id
                             ? $credit->episode->link
                             : $credit->podcast->link,
-                        'title' => $credit->episode
+                        'title' => $credit->episode_id
                             ? (count($allPodcasts) > 1
                                     ? "{$credit->podcast->title} â–¸ "
                                     : '') .
diff --git a/app/Controllers/Platform.php b/app/Controllers/Platform.php
index cca7711424..b4ba11debd 100644
--- a/app/Controllers/Platform.php
+++ b/app/Controllers/Platform.php
@@ -8,6 +8,8 @@
 
 namespace App\Controllers;
 
+use CodeIgniter\HTTP\ResponseInterface;
+use App\Models\PlatformModel;
 use CodeIgniter\Controller;
 
 /*
@@ -15,9 +17,9 @@ use CodeIgniter\Controller;
  */
 class Platform extends Controller
 {
-    public function index()
+    public function index(): ResponseInterface
     {
-        $model = new \App\Models\PlatformModel();
+        $model = new PlatformModel();
 
         return $this->response->setJSON($model->getPlatforms());
     }
diff --git a/app/Controllers/Podcast.php b/app/Controllers/Podcast.php
index c9e735805c..0144e14460 100644
--- a/app/Controllers/Podcast.php
+++ b/app/Controllers/Podcast.php
@@ -18,24 +18,24 @@ class Podcast extends BaseController
     use AnalyticsTrait;
 
     /**
-     * @var \App\Entities\Podcast|null
+     * @var Podcast
      */
     protected $podcast;
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->podcast = (new PodcastModel())->getPodcastByName(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (count($params) === 0) {
+            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+        }
+
+        if (
+            $this->podcast = (new PodcastModel())->getPodcastByName($params[0])
+        ) {
             unset($params[0]);
+            return $this->$method(...$params);
         }
 
-        return $this->$method(...$params);
+        throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
     }
 
     public function activity()
diff --git a/app/Database/Migrations/2020-05-29-152000_add_categories.php b/app/Database/Migrations/2020-05-29-152000_add_categories.php
index 900f5b246e..7f56e55bb5 100644
--- a/app/Database/Migrations/2020-05-29-152000_add_categories.php
+++ b/app/Database/Migrations/2020-05-29-152000_add_categories.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddCategories extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -45,7 +45,7 @@ class AddCategories extends Migration
         $this->forge->createTable('categories');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('categories');
     }
diff --git a/app/Database/Migrations/2020-05-30-101000_add_languages.php b/app/Database/Migrations/2020-05-30-101000_add_languages.php
index 6568990681..b56f42bfad 100644
--- a/app/Database/Migrations/2020-05-30-101000_add_languages.php
+++ b/app/Database/Migrations/2020-05-30-101000_add_languages.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddLanguages extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'code' => [
@@ -32,7 +32,7 @@ class AddLanguages extends Migration
         $this->forge->createTable('languages');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('languages');
     }
diff --git a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
index 675fa83c5c..772d5127a8 100644
--- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
+++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPodcasts extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -204,7 +204,7 @@ class AddPodcasts extends Migration
         $this->forge->createTable('podcasts');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('podcasts');
     }
diff --git a/app/Database/Migrations/2020-06-05-170000_add_episodes.php b/app/Database/Migrations/2020-06-05-170000_add_episodes.php
index cafca23779..c8d55e716b 100644
--- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php
+++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddEpisodes extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -197,7 +197,7 @@ class AddEpisodes extends Migration
         $this->forge->createTable('episodes');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('episodes');
     }
diff --git a/app/Database/Migrations/2020-06-05-180000_add_soundbites.php b/app/Database/Migrations/2020-06-05-180000_add_soundbites.php
index 1f1aec5373..a448d630b6 100644
--- a/app/Database/Migrations/2020-06-05-180000_add_soundbites.php
+++ b/app/Database/Migrations/2020-06-05-180000_add_soundbites.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddSoundbites extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -82,7 +82,7 @@ class AddSoundbites extends Migration
         $this->forge->createTable('soundbites');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('soundbites');
     }
diff --git a/app/Database/Migrations/2020-06-05-190000_add_platforms.php b/app/Database/Migrations/2020-06-05-190000_add_platforms.php
index fb82e82489..e11adbbbbc 100644
--- a/app/Database/Migrations/2020-06-05-190000_add_platforms.php
+++ b/app/Database/Migrations/2020-06-05-190000_add_platforms.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPlatforms extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'slug' => [
@@ -43,13 +43,13 @@ class AddPlatforms extends Migration
         ]);
         $this->forge->addField('`created_at` timestamp NOT NULL DEFAULT NOW()');
         $this->forge->addField(
-            '`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()'
+            '`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()',
         );
         $this->forge->addPrimaryKey('slug');
         $this->forge->createTable('platforms');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('platforms');
     }
diff --git a/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php b/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php
index 37bb9d5646..90d2ab1a90 100644
--- a/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php
+++ b/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPodcastsUsers extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -50,7 +50,7 @@ class AddPodcastsUsers extends Migration
         $this->forge->createTable('podcasts_users');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('podcasts_users');
     }
diff --git a/app/Database/Migrations/2020-08-17-150000_add_pages.php b/app/Database/Migrations/2020-08-17-150000_add_pages.php
index cd271ab50c..fda65e2a01 100644
--- a/app/Database/Migrations/2020-08-17-150000_add_pages.php
+++ b/app/Database/Migrations/2020-08-17-150000_add_pages.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPages extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -50,7 +50,7 @@ class AddPages extends Migration
         $this->forge->createTable('pages');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('pages');
     }
diff --git a/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php b/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php
index 17c60b7145..4d8b73eedb 100644
--- a/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php
+++ b/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPodcastsCategories extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -45,7 +45,7 @@ class AddPodcastsCategories extends Migration
         $this->forge->createTable('podcasts_categories');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('podcasts_categories');
     }
diff --git a/app/Database/Migrations/2020-12-25-120000_add_persons.php b/app/Database/Migrations/2020-12-25-120000_add_persons.php
index 84b34ccf9a..9a79f711fb 100644
--- a/app/Database/Migrations/2020-12-25-120000_add_persons.php
+++ b/app/Database/Migrations/2020-12-25-120000_add_persons.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPersons extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -73,7 +73,7 @@ class AddPersons extends Migration
         $this->forge->createTable('persons');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('persons');
     }
diff --git a/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php b/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php
index af6c7ee57a..5a714efa2d 100644
--- a/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php
+++ b/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPodcastsPersons extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -64,7 +64,7 @@ class AddPodcastsPersons extends Migration
         $this->forge->createTable('podcasts_persons');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('podcasts_persons');
     }
diff --git a/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php b/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php
index 7cc3091418..512756b5dc 100644
--- a/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php
+++ b/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddEpisodesPersons extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -76,7 +76,7 @@ class AddEpisodesPersons extends Migration
         $this->forge->createTable('episodes_persons');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('episodes_persons');
     }
diff --git a/app/Database/Migrations/2020-12-25-150000_add_credit_view.php b/app/Database/Migrations/2020-12-25-150000_add_credit_view.php
index 68dfd05f79..8ed0f75357 100644
--- a/app/Database/Migrations/2020-12-25-150000_add_credit_view.php
+++ b/app/Database/Migrations/2020-12-25-150000_add_credit_view.php
@@ -14,30 +14,34 @@ use CodeIgniter\Database\Migration;
 
 class AddCreditView extends Migration
 {
-    public function up()
+    public function up(): void
     {
         // Creates View for credit UNION query
         $viewName = $this->db->prefixTable('credits');
-        $personTable = $this->db->prefixTable('persons');
-        $podcastPersonTable = $this->db->prefixTable('podcasts_persons');
-        $episodePersonTable = $this->db->prefixTable('episodes_persons');
+        $personsTable = $this->db->prefixTable('persons');
+        $podcastPersonsTable = $this->db->prefixTable('podcasts_persons');
+        $episodePersonsTable = $this->db->prefixTable('episodes_persons');
+        $episodesTable = $this->db->prefixTable('episodes');
         $createQuery = <<<EOD
-        CREATE VIEW `$viewName` AS
-            SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `$podcastPersonTable`
-                INNER JOIN `$personTable`
-                    ON (`person_id`=`$personTable`.`id`)
+        CREATE VIEW `{$viewName}` AS
+            SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `{$podcastPersonsTable}`
+                INNER JOIN `{$personsTable}`
+                    ON (`person_id`=`{$personsTable}`.`id`)
             UNION
-            SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, `episode_id` FROM `$episodePersonTable`
-                INNER JOIN `$personTable`
-                    ON (`person_id`=`$personTable`.`id`)
+            SELECT `person_group`, `person_id`, `full_name`, `person_role`, {$episodePersonsTable}.`podcast_id`, `episode_id` FROM `{$episodePersonsTable}`
+                INNER JOIN `{$personsTable}`
+                    ON (`person_id`=`{$personsTable}`.`id`)
+                INNER JOIN `{$episodesTable}`
+                    ON (`episode_id`=`{$episodesTable}`.`id`)
+            WHERE `{$episodesTable}`.published_at <= NOW()
             ORDER BY `person_group`, `full_name`, `person_role`, `podcast_id`, `episode_id`;
         EOD;
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $viewName = $this->db->prefixTable('credits');
-        $this->db->query("DROP VIEW IF EXISTS `$viewName`");
+        $this->db->query("DROP VIEW IF EXISTS `{$viewName}`");
     }
 }
diff --git a/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php b/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php
index 8278f5a9fb..e6821fbe65 100644
--- a/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php
+++ b/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php
@@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration;
 
 class AddEpisodeIdToNotes extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $prefix = $this->db->getPrefix();
 
         $createQuery = <<<SQL
-            ALTER TABLE ${prefix}activitypub_notes
+            ALTER TABLE {$prefix}activitypub_notes
             ADD COLUMN `episode_id` INT UNSIGNED NULL AFTER `replies_count`,
-            ADD FOREIGN KEY ${prefix}activitypub_notes_episode_id_foreign(episode_id) REFERENCES ${prefix}episodes(id) ON DELETE CASCADE;
+            ADD FOREIGN KEY {$prefix}activitypub_notes_episode_id_foreign(episode_id) REFERENCES {$prefix}episodes(id) ON DELETE CASCADE;
         SQL;
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropForeignKey(
             'activitypub_notes',
diff --git a/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php b/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php
index 6751254279..4c5ffec55d 100644
--- a/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php
+++ b/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php
@@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration;
 
 class AddCreatedByToNotes extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $prefix = $this->db->getPrefix();
 
         $createQuery = <<<SQL
-            ALTER TABLE ${prefix}activitypub_notes
+            ALTER TABLE {$prefix}activitypub_notes
             ADD COLUMN `created_by` INT UNSIGNED AFTER `episode_id`,
-            ADD FOREIGN KEY ${prefix}activitypub_notes_created_by_foreign(created_by) REFERENCES ${prefix}users(id) ON DELETE CASCADE;
+            ADD FOREIGN KEY {$prefix}activitypub_notes_created_by_foreign(created_by) REFERENCES {$prefix}users(id) ON DELETE CASCADE;
         SQL;
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropForeignKey(
             'activitypub_notes',
diff --git a/app/Database/Seeds/AppSeeder.php b/app/Database/Seeds/AppSeeder.php
index 6dac6af4d6..01262d5052 100644
--- a/app/Database/Seeds/AppSeeder.php
+++ b/app/Database/Seeds/AppSeeder.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
 
 class AppSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $this->call('AuthSeeder');
         $this->call('CategorySeeder');
diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php
index 455040f11c..0a0bd086eb 100644
--- a/app/Database/Seeds/AuthSeeder.php
+++ b/app/Database/Seeds/AuthSeeder.php
@@ -15,6 +15,9 @@ use CodeIgniter\Database\Seeder;
 
 class AuthSeeder extends Seeder
 {
+    /**
+     * @var array<string, string>[]
+     */
     protected $groups = [
         [
             'name' => 'superadmin',
@@ -37,6 +40,8 @@ class AuthSeeder extends Seeder
      *      ...
      * ]
      * ```
+     *
+     * @var array<string, array<string, string|array>[]>
      */
     protected $permissions = [
         'users' => [
@@ -249,26 +254,16 @@ class AuthSeeder extends Seeder
         ],
     ];
 
-    static function getGroupIdByName($name, $dataGroups)
-    {
-        foreach ($dataGroups as $group) {
-            if ($group['name'] === $name) {
-                return $group['id'];
-            }
-        }
-        return null;
-    }
-
-    public function run()
+    public function run(): void
     {
         $groupId = 0;
         $dataGroups = [];
         foreach ($this->groups as $group) {
-            array_push($dataGroups, [
+            $dataGroups[] = [
                 'id' => ++$groupId,
                 'name' => $group['name'],
                 'description' => $group['description'],
-            ]);
+            ];
         }
 
         // Map permissions to a format the `auth_permissions` table expects
@@ -277,21 +272,21 @@ class AuthSeeder extends Seeder
         $permissionId = 0;
         foreach ($this->permissions as $context => $actions) {
             foreach ($actions as $action) {
-                array_push($dataPermissions, [
+                $dataPermissions[] = [
                     'id' => ++$permissionId,
                     'name' => $context . '-' . $action['name'],
                     'description' => $action['description'],
-                ]);
+                ];
 
                 foreach ($action['has_permission'] as $role) {
                     // link permission to specified groups
-                    array_push($dataGroupsPermissions, [
+                    $dataGroupsPermissions[] = [
                         'group_id' => $this->getGroupIdByName(
                             $role,
                             $dataGroups,
                         ),
                         'permission_id' => $permissionId,
-                    ]);
+                    ];
                 }
             }
         }
@@ -309,4 +304,16 @@ class AuthSeeder extends Seeder
             ->ignore(true)
             ->insertBatch($dataGroupsPermissions);
     }
+    /**
+     * @param array<string, string|int>[] $dataGroups
+     */
+    static function getGroupIdByName(string $name, array $dataGroups): int
+    {
+        foreach ($dataGroups as $group) {
+            if ($group['name'] === $name) {
+                return $group['id'];
+            }
+        }
+        return null;
+    }
 }
diff --git a/app/Database/Seeds/CategorySeeder.php b/app/Database/Seeds/CategorySeeder.php
index 0fea4e51e1..483b8fe602 100644
--- a/app/Database/Seeds/CategorySeeder.php
+++ b/app/Database/Seeds/CategorySeeder.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
 
 class CategorySeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $data = [
             [
diff --git a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php
index 7adb67fad8..140b61ff87 100644
--- a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php
+++ b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php
@@ -11,6 +11,8 @@
 
 namespace App\Database\Seeds;
 
+use GeoIp2\Database\Reader;
+use GeoIp2\Exception\AddressNotFoundException;
 use App\Models\PodcastModel;
 use App\Models\EpisodeModel;
 
@@ -18,7 +20,7 @@ use CodeIgniter\Database\Seeder;
 
 class FakePodcastsAnalyticsSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $podcast = (new PodcastModel())->first();
 
@@ -27,6 +29,8 @@ class FakePodcastsAnalyticsSeeder extends Seeder
                 'https://raw.githubusercontent.com/opawg/user-agents/master/src/user-agents.json',
             ),
             true,
+            512,
+            JSON_THROW_ON_ERROR,
         );
 
         $jsonRSSUserAgents = json_decode(
@@ -34,6 +38,8 @@ class FakePodcastsAnalyticsSeeder extends Seeder
                 'https://raw.githubusercontent.com/opawg/podcast-rss-useragents/master/src/rss-ua.json',
             ),
             true,
+            512,
+            JSON_THROW_ON_ERROR,
         );
 
         if ($podcast) {
@@ -68,7 +74,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
                     for (
                         $num_line = 0;
                         $num_line < rand(1, $proba1);
-                        $num_line++
+                        ++$num_line
                     ) {
                         $proba2 = floor(exp(6 - $age / 20)) + 10;
 
@@ -96,7 +102,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
                             '.' .
                             rand(0, 255);
 
-                        $cityReader = new \GeoIp2\Database\Reader(
+                        $cityReader = new Reader(
                             WRITEPATH .
                                 'uploads/GeoLite2-City/GeoLite2-City.mmdb',
                         );
@@ -117,7 +123,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
                                 : $city->subdivisions[0]->isoCode;
                             $latitude = round($city->location->latitude, 3);
                             $longitude = round($city->location->longitude, 3);
-                        } catch (\GeoIp2\Exception\AddressNotFoundException $ex) {
+                        } catch (AddressNotFoundException $addressNotFoundException) {
                             //Bad luck, bad IP, nothing to do.
                         }
 
diff --git a/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php b/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php
index af6661ad4a..2534d968a4 100644
--- a/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php
+++ b/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php
@@ -18,6 +18,9 @@ use CodeIgniter\Database\Seeder;
 
 class FakeWebsiteAnalyticsSeeder extends Seeder
 {
+    /**
+     * @var string[]
+     */
     protected $keywords = [
         'all the smoke podcast',
         'apple podcast',
@@ -70,6 +73,10 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
         'wind of change podcast',
         'your own backyard podcast',
     ];
+
+    /**
+     * @var string[]
+     */
     protected $domains = [
         '360.cn ',
         'adobe.com ',
@@ -123,6 +130,9 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
         'zoom.us ',
     ];
 
+    /**
+     * @var string[]
+     */
     protected $browsers = [
         'Android Browser',
         'Avast Secure Browser',
@@ -168,7 +178,7 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
         'WOSBrowser',
     ];
 
-    public function run()
+    public function run(): void
     {
         $podcast = (new PodcastModel())->first();
 
@@ -201,7 +211,7 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
                     for (
                         $num_line = 0;
                         $num_line < rand(1, $proba1);
-                        $num_line++
+                        ++$num_line
                     ) {
                         $proba2 = floor(exp(6 - $age / 20)) + 10;
 
diff --git a/app/Database/Seeds/LanguageSeeder.php b/app/Database/Seeds/LanguageSeeder.php
index b2912c3f65..0c7189d514 100644
--- a/app/Database/Seeds/LanguageSeeder.php
+++ b/app/Database/Seeds/LanguageSeeder.php
@@ -21,7 +21,7 @@ use CodeIgniter\Database\Seeder;
 
 class LanguageSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $data = [
             ['code' => 'aa', 'native_name' => 'Afaraf'],
@@ -163,7 +163,7 @@ class LanguageSeeder extends Seeder
                 'native_name' => 'Gàidhlig',
             ],
             ['code' => 'gl', 'native_name' => 'Galego'],
-            ['code' => 'gn', 'native_name' => 'Avañe\'ẽ'],
+            ['code' => 'gn', 'native_name' => "Avañe'ẽ"],
             ['code' => 'gu', 'native_name' => 'ગુજરાતી'],
             [
                 'code' => 'gv',
@@ -436,7 +436,7 @@ class LanguageSeeder extends Seeder
             ],
             [
                 'code' => 'sm',
-                'native_name' => 'gagana fa\'a Samoa',
+                'native_name' => "gagana fa'a Samoa",
             ],
             ['code' => 'sn', 'native_name' => 'chiShona'],
             [
diff --git a/app/Database/Seeds/PlatformSeeder.php b/app/Database/Seeds/PlatformSeeder.php
index 309b767caf..53cf749a51 100644
--- a/app/Database/Seeds/PlatformSeeder.php
+++ b/app/Database/Seeds/PlatformSeeder.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
 
 class PlatformSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $data = [
             [
diff --git a/app/Database/Seeds/TestSeeder.php b/app/Database/Seeds/TestSeeder.php
index 649b98bb09..364feb5917 100644
--- a/app/Database/Seeds/TestSeeder.php
+++ b/app/Database/Seeds/TestSeeder.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
 
 class TestSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         /** Inserts an active user with the following credentials:
          *      username: admin
@@ -29,6 +29,7 @@ class TestSeeder extends Seeder
                 '$2y$10$TXJEHX/djW8jtzgpDVf7dOOCGo5rv1uqtAYWdwwwkttQcDkAeB2.6',
             'active' => 1,
         ]);
+
         $this->db
             ->table('auth_groups_users')
             ->insert(['group_id' => 1, 'user_id' => 1]);
diff --git a/app/Entities/Actor.php b/app/Entities/Actor.php
index 78515a706f..e19c00c1ce 100644
--- a/app/Entities/Actor.php
+++ b/app/Entities/Actor.php
@@ -9,6 +9,7 @@
 namespace App\Entities;
 
 use App\Models\PodcastModel;
+use RuntimeException;
 
 class Actor extends \ActivityPub\Entities\Actor
 {
@@ -30,7 +31,7 @@ class Actor extends \ActivityPub\Entities\Actor
     public function getPodcast()
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Actor must be created before getting associated podcast.',
             );
         }
diff --git a/app/Entities/Category.php b/app/Entities/Category.php
index d58e143b48..72edbe9ec3 100644
--- a/app/Entities/Category.php
+++ b/app/Entities/Category.php
@@ -9,15 +9,18 @@
 namespace App\Entities;
 
 use App\Models\CategoryModel;
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Category extends Entity
 {
     /**
-     * @var \App\Entity\Category|null
+     * @var Category|null
      */
     protected $parent;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'parent_id' => 'integer',
@@ -26,12 +29,12 @@ class Category extends Entity
         'google_category' => 'string',
     ];
 
-    public function getParent()
+    public function getParent(): ?Category
     {
-        $parentId = $this->attributes['parent_id'];
+        if (empty($this->parent_id)) {
+            return null;
+        }
 
-        return $parentId != 0
-            ? (new CategoryModel())->getCategoryById($parentId)
-            : null;
+        return (new CategoryModel())->getCategoryById($this->parent_id);
     }
 }
diff --git a/app/Entities/Credit.php b/app/Entities/Credit.php
index 94dd5f4a94..f323cbb1c3 100644
--- a/app/Entities/Credit.php
+++ b/app/Entities/Credit.php
@@ -8,26 +8,26 @@
 
 namespace App\Entities;
 
+use RuntimeException;
 use App\Models\PersonModel;
 use App\Models\PodcastModel;
 use App\Models\EpisodeModel;
-
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Credit extends Entity
 {
     /**
-     * @var \App\Entities\Person
+     * @var Person
      */
     protected $person;
 
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
     /**
-     * @var \App\Entities\Episode|null
+     * @var Episode|null
      */
     protected $episode;
 
@@ -41,35 +41,47 @@ class Credit extends Entity
      */
     protected $role_label;
 
-    public function getPodcast()
+    /**
+     * @var array<string, string>
+     */
+    protected $casts = [
+        'person_group' => 'string',
+        'person_role' => 'string',
+        'person_id' => 'integer',
+        'full_name' => 'integer',
+        'podcast_id' => 'integer',
+        'episode_id' => '?integer',
+    ];
+
+    public function getPodcast(): Podcast
     {
         return (new PodcastModel())->getPodcastById(
             $this->attributes['podcast_id'],
         );
     }
 
-    public function getEpisode()
+    public function getEpisode(): ?Episode
     {
         if (empty($this->episode_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Credit must have episode_id before getting episode.',
             );
         }
 
         if (empty($this->episode)) {
             $this->episode = (new EpisodeModel())->getPublishedEpisodeById(
-                $this->episode_id,
                 $this->podcast_id,
+                $this->episode_id,
             );
         }
 
         return $this->episode;
     }
 
-    public function getPerson()
+    public function getPerson(): Person
     {
         if (empty($this->person_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Credit must have person_id before getting person.',
             );
         }
@@ -83,23 +95,27 @@ class Credit extends Entity
         return $this->person;
     }
 
-    public function getGroupLabel()
+    public function getGroupLabel(): ?string
     {
         if (empty($this->person_group)) {
             return null;
-        } else {
-            return lang("PersonsTaxonomy.persons.{$this->person_group}.label");
         }
+
+        return lang("PersonsTaxonomy.persons.{$this->person_group}.label");
     }
 
-    public function getRoleLabel()
+    public function getRoleLabel(): ?string
     {
-        if (empty($this->person_group) || empty($this->person_role)) {
+        if (empty($this->person_group)) {
+            return null;
+        }
+
+        if (empty($this->person_role)) {
             return null;
-        } else {
-            return lang(
-                "PersonsTaxonomy.persons.{$this->person_group}.roles.{$this->person_role}.label",
-            );
         }
+
+        return lang(
+            "PersonsTaxonomy.persons.{$this->person_group}.roles.{$this->person_role}.label",
+        );
     }
 }
diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php
index 338e987989..d22974218c 100644
--- a/app/Entities/Episode.php
+++ b/app/Entities/Episode.php
@@ -8,20 +8,25 @@
 
 namespace App\Entities;
 
+use App\Libraries\Image;
+use App\Libraries\SimpleRSSElement;
 use App\Models\PodcastModel;
 use App\Models\SoundbiteModel;
 use App\Models\EpisodePersonModel;
 use App\Models\NoteModel;
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 use CodeIgniter\Files\Exceptions\FileNotFoundException;
+use CodeIgniter\Files\File;
 use CodeIgniter\HTTP\Exceptions\HTTPException;
+use CodeIgniter\HTTP\Files\UploadedFile;
 use CodeIgniter\I18n\Time;
 use League\CommonMark\CommonMarkConverter;
+use RuntimeException;
 
 class Episode extends Entity
 {
     /**
-     * @var \App\Entities\Podcast
+     * @var Podcast
      */
     protected $podcast;
 
@@ -31,22 +36,22 @@ class Episode extends Entity
     protected $link;
 
     /**
-     * @var \App\Libraries\Image
+     * @var Image
      */
     protected $image;
 
     /**
-     * @var \CodeIgniter\Files\File
+     * @var File
      */
     protected $audioFile;
 
     /**
-     * @var \CodeIgniter\Files\File
+     * @var File
      */
     protected $transcript_file;
 
     /**
-     * @var \CodeIgniter\Files\File
+     * @var File
      */
     protected $chapters_file;
 
@@ -71,17 +76,17 @@ class Episode extends Entity
     protected $audio_file_opengraph_url;
 
     /**
-     * @var \App\Entities\EpisodePerson[]
+     * @var EpisodePerson[]
      */
     protected $persons;
 
     /**
-     * @var \App\Entities\Soundbite[]
+     * @var Soundbite[]
      */
     protected $soundbites;
 
     /**
-     * @var \App\Entities\Note[]
+     * @var Note[]
      */
     protected $notes;
 
@@ -156,15 +161,13 @@ class Episode extends Entity
     /**
      * Saves an episode image
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image
-     *
+     * @param UploadedFile|File $image
      */
     public function setImage($image)
     {
         if (
             !empty($image) &&
-            (!($image instanceof \CodeIgniter\HTTP\Files\UploadedFile) ||
-                $image->isValid())
+            (!($image instanceof UploadedFile) || $image->isValid())
         ) {
             helper('media');
 
@@ -175,7 +178,7 @@ class Episode extends Entity
                 'podcasts/' . $this->getPodcast()->name,
                 $this->attributes['slug'],
             );
-            $this->image = new \App\Libraries\Image(
+            $this->image = new Image(
                 $this->attributes['image_path'],
                 $this->attributes['image_mimetype'],
             );
@@ -185,13 +188,10 @@ class Episode extends Entity
         return $this;
     }
 
-    public function getImage(): \App\Libraries\Image
+    public function getImage(): Image
     {
         if ($imagePath = $this->attributes['image_path']) {
-            return new \App\Libraries\Image(
-                $imagePath,
-                $this->attributes['image_mimetype'],
-            );
+            return new Image($imagePath, $this->attributes['image_mimetype']);
         }
         return $this->getPodcast()->image;
     }
@@ -199,15 +199,14 @@ class Episode extends Entity
     /**
      * Saves an audio file
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $audioFile
+     * @param UploadedFile|File $audioFile
      *
      */
     public function setAudioFile($audioFile = null)
     {
         if (
             !empty($audioFile) &&
-            (!($audioFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) ||
-                $audioFile->isValid())
+            (!($audioFile instanceof UploadedFile) || $audioFile->isValid())
         ) {
             helper(['media', 'id3']);
 
@@ -234,16 +233,14 @@ class Episode extends Entity
     /**
      * Saves an episode transcript file
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $transcriptFile
+     * @param UploadedFile|File $transcriptFile
      *
      */
     public function setTranscriptFile($transcriptFile)
     {
         if (
             !empty($transcriptFile) &&
-            (!(
-                $transcriptFile instanceof \CodeIgniter\HTTP\Files\UploadedFile
-            ) ||
+            (!($transcriptFile instanceof UploadedFile) ||
                 $transcriptFile->isValid())
         ) {
             helper('media');
@@ -261,14 +258,14 @@ class Episode extends Entity
     /**
      * Saves an episode chapters file
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $chaptersFile
+     * @param UploadedFile|File $chaptersFile
      *
      */
     public function setChaptersFile($chaptersFile)
     {
         if (
             !empty($chaptersFile) &&
-            (!($chaptersFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) ||
+            (!($chaptersFile instanceof UploadedFile) ||
                 $chaptersFile->isValid())
         ) {
             helper('media');
@@ -287,7 +284,7 @@ class Episode extends Entity
     {
         helper('media');
 
-        return new \CodeIgniter\Files\File(media_path($this->audio_file_path));
+        return new File(media_path($this->audio_file_path));
     }
 
     public function getTranscriptFile()
@@ -295,7 +292,7 @@ class Episode extends Entity
         if ($this->attributes['transcript_file_path']) {
             helper('media');
 
-            return new \CodeIgniter\Files\File(
+            return new File(
                 media_path($this->attributes['transcript_file_path']),
             );
         }
@@ -308,7 +305,7 @@ class Episode extends Entity
         if ($this->attributes['chapters_file_path']) {
             helper('media');
 
-            return new \CodeIgniter\Files\File(
+            return new File(
                 media_path($this->attributes['chapters_file_path']),
             );
         }
@@ -320,7 +317,7 @@ class Episode extends Entity
     {
         helper('media');
 
-        return media_url($this->audio_file_path);
+        return media_base_url($this->audio_file_path);
     }
 
     public function getAudioFileAnalyticsUrl()
@@ -359,7 +356,7 @@ class Episode extends Entity
     public function getTranscriptFileUrl()
     {
         if ($this->attributes['transcript_file_path']) {
-            return media_url($this->attributes['transcript_file_path']);
+            return media_base_url($this->attributes['transcript_file_path']);
         } else {
             return $this->attributes['transcript_file_remote_url'];
         }
@@ -368,17 +365,14 @@ class Episode extends Entity
     /**
      * Gets chapters file url from chapters file uri if it exists
      * or returns the chapters_file_remote_url which can be null.
-     *
-     * @return mixed
-     * @throws HTTPException
      */
-    public function getChaptersFileUrl()
+    public function getChaptersFileUrl(): ?string
     {
-        if ($this->attributes['chapters_file_path']) {
-            return media_url($this->attributes['chapters_file_path']);
-        } else {
-            return $this->attributes['chapters_file_remote_url'];
+        if ($this->chapters_file_path) {
+            return media_base_url($this->chapters_file_path);
         }
+
+        return $this->chapters_file_remote_url;
     }
 
     /**
@@ -389,7 +383,7 @@ class Episode extends Entity
     public function getPersons()
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Episode must be created before getting persons.',
             );
         }
@@ -412,7 +406,7 @@ class Episode extends Entity
     public function getSoundbites()
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Episode must be created before getting soundbites.',
             );
         }
@@ -430,7 +424,7 @@ class Episode extends Entity
     public function getNotes()
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Episode must be created before getting soundbites.',
             );
         }
@@ -586,23 +580,24 @@ class Episode extends Entity
      */
     function getCustomRssString()
     {
-        helper('rss');
-        if (empty($this->attributes['custom_rss'])) {
+        if (empty($this->custom_rss)) {
             return '';
-        } else {
-            $xmlNode = (new \App\Libraries\SimpleRSSElement(
-                '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
-            ))
-                ->addChild('channel')
-                ->addChild('item');
-            array_to_rss(
-                [
-                    'elements' => $this->custom_rss,
-                ],
-                $xmlNode,
-            );
-            return str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
         }
+
+        helper('rss');
+
+        $xmlNode = (new SimpleRSSElement(
+            '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
+        ))
+            ->addChild('channel')
+            ->addChild('item');
+        array_to_rss(
+            [
+                'elements' => $this->custom_rss,
+            ],
+            $xmlNode,
+        );
+        return str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
     }
 
     /**
@@ -613,6 +608,10 @@ class Episode extends Entity
      */
     function setCustomRssString($customRssString)
     {
+        if (empty($customRssString)) {
+            return $this;
+        }
+
         helper('rss');
         $customRssArray = rss_to_array(
             simplexml_load_string(
@@ -621,6 +620,7 @@ class Episode extends Entity
                     '</item></channel></rss>',
             ),
         )['elements'][0]['elements'][0];
+
         if (array_key_exists('elements', $customRssArray)) {
             $this->attributes['custom_rss'] = json_encode(
                 $customRssArray['elements'],
@@ -628,6 +628,8 @@ class Episode extends Entity
         } else {
             $this->attributes['custom_rss'] = null;
         }
+
+        return $this;
     }
 
     function getPartnerLink($serviceSlug = null)
diff --git a/app/Entities/EpisodePerson.php b/app/Entities/EpisodePerson.php
index 6c0a6388bd..dff7be1b13 100644
--- a/app/Entities/EpisodePerson.php
+++ b/app/Entities/EpisodePerson.php
@@ -8,16 +8,19 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 use App\Models\PersonModel;
 
 class EpisodePerson extends Entity
 {
     /**
-     * @var \App\Entities\Person
+     * @var Person
      */
     protected $person;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'podcast_id' => 'integer',
@@ -30,7 +33,7 @@ class EpisodePerson extends Entity
     public function getPerson()
     {
         return (new PersonModel())->getPersonById(
-            $this->attributes['person_id']
+            $this->attributes['person_id'],
         );
     }
 }
diff --git a/app/Entities/Language.php b/app/Entities/Language.php
index aa683fd73d..ea9b4a53a5 100644
--- a/app/Entities/Language.php
+++ b/app/Entities/Language.php
@@ -8,10 +8,13 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Language extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'code' => 'string',
         'native_name' => 'string',
diff --git a/app/Entities/Note.php b/app/Entities/Note.php
index 6ec4802990..223a9c2092 100644
--- a/app/Entities/Note.php
+++ b/app/Entities/Note.php
@@ -9,11 +9,12 @@
 namespace App\Entities;
 
 use App\Models\EpisodeModel;
+use RuntimeException;
 
 class Note extends \ActivityPub\Entities\Note
 {
     /**
-     * @var \App\Entities\Episode|null
+     * @var Episode|null
      */
     protected $episode;
 
@@ -40,7 +41,7 @@ class Note extends \ActivityPub\Entities\Note
     public function getEpisode()
     {
         if (empty($this->episode_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Note must have an episode_id before getting episode.',
             );
         }
diff --git a/app/Entities/Page.php b/app/Entities/Page.php
index bb40d68a02..518edf6649 100644
--- a/app/Entities/Page.php
+++ b/app/Entities/Page.php
@@ -8,7 +8,7 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 use League\CommonMark\CommonMarkConverter;
 
 class Page extends Entity
@@ -23,6 +23,9 @@ class Page extends Entity
      */
     protected $content_html;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'title' => 'string',
@@ -32,10 +35,10 @@ class Page extends Entity
 
     public function getLink()
     {
-        return base_url($this->attributes['slug']);
+        return url_to('page', $this->attributes['slug']);
     }
 
-    public function getContentHtml()
+    public function getContentHtml(): string
     {
         $converter = new CommonMarkConverter([
             'html_input' => 'strip',
diff --git a/app/Entities/Person.php b/app/Entities/Person.php
index d6c9c50fdd..2fdfbc33d8 100644
--- a/app/Entities/Person.php
+++ b/app/Entities/Person.php
@@ -8,15 +8,21 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use App\Libraries\Image;
+use CodeIgniter\HTTP\Files\UploadedFile;
+use CodeIgniter\Files\File;
+use CodeIgniter\Entity\Entity;
 
 class Person extends Entity
 {
     /**
-     * @var \App\Libraries\Image
+     * @var Image
      */
     protected $image;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'full_name' => 'string',
@@ -31,12 +37,11 @@ class Person extends Entity
     /**
      * Saves a picture in `public/media/persons/`
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image
-     *
+     * @param UploadedFile|File|null $image
      */
-    public function setImage($image = null)
+    public function setImage($image = null): self
     {
-        if ($image) {
+        if ($image !== null) {
             helper('media');
 
             $this->attributes['image_mimetype'] = $image->getMimeType();
@@ -45,7 +50,7 @@ class Person extends Entity
                 'persons',
                 $this->attributes['unique_name'],
             );
-            $this->image = new \App\Libraries\Image(
+            $this->image = new Image(
                 $this->attributes['image_path'],
                 $this->attributes['image_mimetype'],
             );
@@ -55,9 +60,9 @@ class Person extends Entity
         return $this;
     }
 
-    public function getImage()
+    public function getImage(): Image
     {
-        return new \App\Libraries\Image(
+        return new Image(
             $this->attributes['image_path'],
             $this->attributes['image_mimetype'],
         );
diff --git a/app/Entities/Platform.php b/app/Entities/Platform.php
index a1080da46f..9cc5662d03 100644
--- a/app/Entities/Platform.php
+++ b/app/Entities/Platform.php
@@ -8,10 +8,13 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Platform extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'slug' => 'string',
         'type' => 'string',
diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php
index 740a0134ad..7bd77a15eb 100644
--- a/app/Entities/Podcast.php
+++ b/app/Entities/Podcast.php
@@ -8,14 +8,18 @@
 
 namespace App\Entities;
 
-use ActivityPub\Models\ActorModel;
+use App\Libraries\Image;
+use App\Libraries\SimpleRSSElement;
 use App\Models\CategoryModel;
 use App\Models\EpisodeModel;
 use App\Models\PlatformModel;
 use App\Models\PodcastPersonModel;
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 use App\Models\UserModel;
+use CodeIgniter\Files\File;
+use CodeIgniter\HTTP\Files\UploadedFile;
 use League\CommonMark\CommonMarkConverter;
+use RuntimeException;
 
 class Podcast extends Entity
 {
@@ -25,32 +29,32 @@ class Podcast extends Entity
     protected $link;
 
     /**
-     * @var \ActivityPub\Entities\Actor
+     * @var Actor
      */
     protected $actor;
 
     /**
-     * @var \App\Libraries\Image
+     * @var Image
      */
     protected $image;
 
     /**
-     * @var \App\Entities\Episode[]
+     * @var Episode[]
      */
     protected $episodes;
 
     /**
-     * @var \App\Entities\PodcastPerson[]
+     * @var PodcastPerson[]
      */
     protected $persons;
 
     /**
-     * @var \App\Entities\Category
+     * @var Category
      */
     protected $category;
 
     /**
-     * @var \App\Entities\Category[]
+     * @var Category[]
      */
     protected $other_categories;
 
@@ -60,22 +64,22 @@ class Podcast extends Entity
     protected $other_categories_ids;
 
     /**
-     * @var \App\Entities\User[]
+     * @var User[]
      */
     protected $contributors;
 
     /**
-     * @var \App\Entities\Platform
+     * @var Platform
      */
     protected $podcastingPlatforms;
 
     /**
-     * @var \App\Entities\Platform
+     * @var Platform
      */
     protected $socialPlatforms;
 
     /**
-     * @var \App\Entities\Platform
+     * @var Platform
      */
     protected $fundingPlatforms;
 
@@ -132,12 +136,12 @@ class Podcast extends Entity
     /**
      * Returns the podcast actor
      *
-     * @return \App\Entities\Actor
+     * @return Actor
      */
-    public function getActor()
+    public function getActor(): Actor
     {
         if (!$this->attributes['actor_id']) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must have an actor_id before getting actor.',
             );
         }
@@ -152,10 +156,9 @@ class Podcast extends Entity
     /**
      * Saves a cover image to the corresponding podcast folder in `public/media/podcast_name/`
      *
-     * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image
-     *
+     * @param UploadedFile|File $image
      */
-    public function setImage($image = null)
+    public function setImage($image = null): self
     {
         if ($image) {
             helper('media');
@@ -167,7 +170,7 @@ class Podcast extends Entity
                 'cover',
             );
 
-            $this->image = new \App\Libraries\Image(
+            $this->image = new Image(
                 $this->attributes['image_path'],
                 $this->attributes['image_mimetype'],
             );
@@ -177,20 +180,20 @@ class Podcast extends Entity
         return $this;
     }
 
-    public function getImage()
+    public function getImage(): Image
     {
-        return new \App\Libraries\Image(
+        return new Image(
             $this->attributes['image_path'],
             $this->attributes['image_mimetype'],
         );
     }
 
-    public function getLink()
+    public function getLink(): string
     {
         return url_to('podcast-activity', $this->attributes['name']);
     }
 
-    public function getFeedUrl()
+    public function getFeedUrl(): string
     {
         return url_to('podcast_feed', $this->attributes['name']);
     }
@@ -198,12 +201,12 @@ class Podcast extends Entity
     /**
      * Returns the podcast's episodes
      *
-     * @return \App\Entities\Episode[]
+     * @return Episode[]
      */
-    public function getEpisodes()
+    public function getEpisodes(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting episodes.',
             );
         }
@@ -221,12 +224,12 @@ class Podcast extends Entity
     /**
      * Returns the podcast's persons
      *
-     * @return \App\Entities\PodcastPerson[]
+     * @return PodcastPerson[]
      */
-    public function getPersons()
+    public function getPersons(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting persons.',
             );
         }
@@ -243,12 +246,12 @@ class Podcast extends Entity
     /**
      * Returns the podcast category entity
      *
-     * @return \App\Entities\Category
+     * @return Category
      */
-    public function getCategory()
+    public function getCategory(): Category
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting category.',
             );
         }
@@ -265,12 +268,12 @@ class Podcast extends Entity
     /**
      * Returns all podcast contributors
      *
-     * @return \App\Entities\User[]
+     * @return User[]
      */
-    public function getContributors()
+    public function getContributors(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcasts must be created before getting contributors.',
             );
         }
@@ -284,7 +287,7 @@ class Podcast extends Entity
         return $this->contributors;
     }
 
-    public function setDescriptionMarkdown(string $descriptionMarkdown)
+    public function setDescriptionMarkdown(string $descriptionMarkdown): self
     {
         $converter = new CommonMarkConverter([
             'html_input' => 'strip',
@@ -300,8 +303,8 @@ class Podcast extends Entity
     }
 
     public function setEpisodeDescriptionFooterMarkdown(
-        string $episodeDescriptionFooterMarkdown = null
-    ) {
+        ?string $episodeDescriptionFooterMarkdown = null
+    ): self {
         if ($episodeDescriptionFooterMarkdown) {
             $converter = new CommonMarkConverter([
                 'html_input' => 'strip',
@@ -319,7 +322,7 @@ class Podcast extends Entity
         return $this;
     }
 
-    public function getDescription()
+    public function getDescription(): string
     {
         if ($this->description) {
             return $this->description;
@@ -337,12 +340,12 @@ class Podcast extends Entity
     /**
      * Returns the podcast's podcasting platform links
      *
-     * @return \App\Entities\Platform[]
+     * @return Platform[]
      */
-    public function getPodcastingPlatforms()
+    public function getPodcastingPlatforms(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting podcasting platform links.',
             );
         }
@@ -357,33 +360,15 @@ class Podcast extends Entity
         return $this->podcastingPlatforms;
     }
 
-    /**
-     * Returns true if the podcast has podcasting platform links
-     */
-    public function getHasPodcastingPlatforms()
-    {
-        if (empty($this->id)) {
-            throw new \RuntimeException(
-                'Podcast must be created before getting podcasting platform.',
-            );
-        }
-        foreach ($this->getPodcastingPlatforms() as $podcastingPlatform) {
-            if ($podcastingPlatform->is_on_embeddable_player) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Returns the podcast's social platform links
      *
-     * @return \App\Entities\Platform[]
+     * @return Platform[]
      */
-    public function getSocialPlatforms()
+    public function getSocialPlatforms(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting social platform links.',
             );
         }
@@ -398,33 +383,15 @@ class Podcast extends Entity
         return $this->socialPlatforms;
     }
 
-    /**
-     * Returns true if the podcast has social platform links
-     */
-    public function getHasSocialPlatforms()
-    {
-        if (empty($this->id)) {
-            throw new \RuntimeException(
-                'Podcast must be created before getting social platform.',
-            );
-        }
-        foreach ($this->getSocialPlatforms() as $socialPlatform) {
-            if ($socialPlatform->is_on_embeddable_player) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Returns the podcast's funding platform links
      *
-     * @return \App\Entities\Platform[]
+     * @return Platform[]
      */
-    public function getFundingPlatforms()
+    public function getFundingPlatforms(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting funding platform links.',
             );
         }
@@ -440,27 +407,12 @@ class Podcast extends Entity
     }
 
     /**
-     * Returns true if the podcast has social platform links
+     * @return Category[]
      */
-    public function getHasFundingPlatforms()
-    {
-        if (empty($this->id)) {
-            throw new \RuntimeException(
-                'Podcast must be created before getting Funding platform.',
-            );
-        }
-        foreach ($this->getFundingPlatforms() as $fundingPlatform) {
-            if ($fundingPlatform->is_on_embeddable_player) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public function getOtherCategories()
+    public function getOtherCategories(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Podcast must be created before getting other categories.',
             );
         }
@@ -474,7 +426,10 @@ class Podcast extends Entity
         return $this->other_categories;
     }
 
-    public function getOtherCategoriesIds()
+    /**
+     * @return array<int>
+     */
+    public function getOtherCategoriesIds(): array
     {
         if (empty($this->other_categories_ids)) {
             $this->other_categories_ids = array_column(
@@ -488,11 +443,8 @@ class Podcast extends Entity
 
     /**
      * Saves the location name and fetches OpenStreetMap info
-     *
-     * @param string $locationName
-     *
      */
-    public function setLocation($locationName = null)
+    public function setLocation(?string $locationName = null): self
     {
         helper('location');
 
@@ -511,6 +463,7 @@ class Podcast extends Entity
             $this->attributes['location_geo'] = null;
             $this->attributes['location_osmid'] = null;
         }
+
         return $this;
     }
 
@@ -518,39 +471,39 @@ class Podcast extends Entity
      * Get custom rss tag as XML String
      *
      * @return string
-     *
      */
-    function getCustomRssString()
+    function getCustomRssString(): string
     {
-        helper('rss');
         if (empty($this->attributes['custom_rss'])) {
             return '';
-        } else {
-            $xmlNode = (new \App\Libraries\SimpleRSSElement(
-                '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
-            ))->addChild('channel');
-            array_to_rss(
-                [
-                    'elements' => $this->custom_rss,
-                ],
-                $xmlNode,
-            );
-            return str_replace(
-                ['<channel>', '</channel>'],
-                '',
-                $xmlNode->asXML(),
-            );
         }
+
+        helper('rss');
+
+        $xmlNode = (new SimpleRSSElement(
+            '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
+        ))->addChild('channel');
+        array_to_rss(
+            [
+                'elements' => $this->custom_rss,
+            ],
+            $xmlNode,
+        );
+
+        return str_replace(['<channel>', '</channel>'], '', $xmlNode->asXML());
     }
 
     /**
      * Saves custom rss tag into json
      *
      * @param string $customRssString
-     *
      */
-    function setCustomRssString($customRssString)
+    function setCustomRssString($customRssString): self
     {
+        if (empty($customRssString)) {
+            return $this;
+        }
+
         helper('rss');
         $customRssArray = rss_to_array(
             simplexml_load_string(
@@ -559,6 +512,7 @@ class Podcast extends Entity
                     '</channel></rss>',
             ),
         )['elements'][0];
+
         if (array_key_exists('elements', $customRssArray)) {
             $this->attributes['custom_rss'] = json_encode(
                 $customRssArray['elements'],
@@ -566,5 +520,7 @@ class Podcast extends Entity
         } else {
             $this->attributes['custom_rss'] = null;
         }
+
+        return $this;
     }
 }
diff --git a/app/Entities/PodcastPerson.php b/app/Entities/PodcastPerson.php
index 95dec77c51..55481910e9 100644
--- a/app/Entities/PodcastPerson.php
+++ b/app/Entities/PodcastPerson.php
@@ -8,16 +8,19 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 use App\Models\PersonModel;
 
 class PodcastPerson extends Entity
 {
     /**
-     * @var \App\Entities\Person
+     * @var Person
      */
     protected $person;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'podcast_id' => 'integer',
@@ -29,7 +32,7 @@ class PodcastPerson extends Entity
     public function getPerson()
     {
         return (new PersonModel())->getPersonById(
-            $this->attributes['person_id']
+            $this->attributes['person_id'],
         );
     }
 }
diff --git a/app/Entities/Soundbite.php b/app/Entities/Soundbite.php
index 04d8f29396..a12c13ff02 100644
--- a/app/Entities/Soundbite.php
+++ b/app/Entities/Soundbite.php
@@ -8,10 +8,13 @@
 
 namespace App\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Soundbite extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'podcast_id' => 'integer',
@@ -23,7 +26,7 @@ class Soundbite extends Entity
         'updated_by' => 'integer',
     ];
 
-    public function setUpdatedBy(\App\Entities\User $user)
+    public function setUpdatedBy(User $user): self
     {
         $this->attributes['updated_by'] = $user->id;
 
diff --git a/app/Entities/User.php b/app/Entities/User.php
index 2c94df540a..ca48a273df 100644
--- a/app/Entities/User.php
+++ b/app/Entities/User.php
@@ -8,25 +8,29 @@
 
 namespace App\Entities;
 
+use RuntimeException;
 use App\Models\PodcastModel;
 
 class User extends \Myth\Auth\Entities\User
 {
     /**
      * Per-user podcasts
-     * @var \App\Entities\Podcast[]
+     * @var Podcast[]
      */
     protected $podcasts = [];
 
     /**
      * The podcast the user is contributing to
-     * @var \App\Entities\Podcast|null
+     *
+     * @var Podcast|null
      */
-    protected $podcast = null;
+    protected $podcast;
 
     /**
      * Array of field names and the type of value to cast them as
      * when they are accessed.
+     *
+     * @var array<string, string>
      */
     protected $casts = [
         'id' => 'integer',
@@ -39,13 +43,13 @@ class User extends \Myth\Auth\Entities\User
     /**
      * Returns the podcasts the user is contributing to
      *
-     * @return \App\Entities\Podcast[]
+     * @return Podcast[]
      */
-    public function getPodcasts()
+    public function getPodcasts(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
-                'Users must be created before getting podcasts.'
+            throw new RuntimeException(
+                'Users must be created before getting podcasts.',
             );
         }
 
@@ -58,20 +62,18 @@ class User extends \Myth\Auth\Entities\User
 
     /**
      * Returns a podcast the user is contributing to
-     *
-     * @return \App\Entities\Podcast
      */
-    public function getPodcast()
+    public function getPodcast(): Podcast
     {
         if (empty($this->podcast_id)) {
-            throw new \RuntimeException(
-                'Podcast_id must be set before getting podcast.'
+            throw new RuntimeException(
+                'Podcast_id must be set before getting podcast.',
             );
         }
 
         if (empty($this->podcast)) {
             $this->podcast = (new PodcastModel())->getPodcastById(
-                $this->podcast_id
+                $this->podcast_id,
             );
         }
 
diff --git a/app/Filters/PermissionFilter.php b/app/Filters/PermissionFilter.php
index f40c6a499b..929bb9544d 100644
--- a/app/Filters/PermissionFilter.php
+++ b/app/Filters/PermissionFilter.php
@@ -21,10 +21,8 @@ class PermissionFilter implements FilterInterface
      * sent back to the client, allowing for error pages,
      * redirects, etc.
      *
-     * @param \CodeIgniter\HTTP\RequestInterface $request
      * @param array|null                         $params
-     *
-     * @return mixed
+     * @return void|mixed
      */
     public function before(RequestInterface $request, $params = null)
     {
@@ -59,15 +57,14 @@ class PermissionFilter implements FilterInterface
                 count($routerParams) > 0
             ) {
                 if (
-                    $groupId = (new PodcastModel())->getContributorGroupId(
+                    ($groupId = (new PodcastModel())->getContributorGroupId(
                         $authenticate->id(),
-                        $routerParams[0]
-                    )
+                        $routerParams[0],
+                    )) &&
+                    $authorize->groupHasPermission($permission, $groupId)
                 ) {
-                    if ($authorize->groupHasPermission($permission, $groupId)) {
-                        $result = true;
-                        break;
-                    }
+                    $result = true;
+                    break;
                 }
             } elseif (
                 $authorize->hasPermission($permission, $authenticate->id())
@@ -84,31 +81,25 @@ class PermissionFilter implements FilterInterface
                 return redirect()
                     ->to($redirectURL)
                     ->with('error', lang('Auth.notEnoughPrivilege'));
-            } else {
-                throw new PermissionException(lang('Auth.notEnoughPrivilege'));
             }
+            throw new PermissionException(lang('Auth.notEnoughPrivilege'));
         }
     }
 
     //--------------------------------------------------------------------
-
     /**
      * Allows After filters to inspect and modify the response
      * object as needed. This method does not allow any way
      * to stop execution of other after filters, short of
      * throwing an Exception or Error.
      *
-     * @param \CodeIgniter\HTTP\RequestInterface  $request
-     * @param \CodeIgniter\HTTP\ResponseInterface $response
      * @param array|null                          $arguments
-     *
-     * @return void
      */
     public function after(
         RequestInterface $request,
         ResponseInterface $response,
         $arguments = null
-    ) {
+    ): void {
     }
 
     //--------------------------------------------------------------------
diff --git a/app/Helpers/auth_helper.php b/app/Helpers/auth_helper.php
index 4408c0aa7c..fc07555abf 100644
--- a/app/Helpers/auth_helper.php
+++ b/app/Helpers/auth_helper.php
@@ -6,16 +6,15 @@
  * @link       https://castopod.org/
  */
 
+use ActivityPub\Entities\Actor;
 use CodeIgniter\Database\Exceptions\DataException;
 use Config\Services;
 
 if (!function_exists('set_interact_as_actor')) {
     /**
      * Sets the actor id of which the user is acting as
-     *
-     * @return void
      */
-    function set_interact_as_actor($actorId)
+    function set_interact_as_actor($actorId): void
     {
         $authenticate = Services::authentication();
         $authenticate->check();
@@ -28,10 +27,8 @@ if (!function_exists('set_interact_as_actor')) {
 if (!function_exists('remove_interact_as_actor')) {
     /**
      * Removes the actor id of which the user is acting as
-     *
-     * @return void
      */
-    function remove_interact_as_actor()
+    function remove_interact_as_actor(): void
     {
         $session = session();
         $session->remove('interact_as_actor_id');
@@ -41,10 +38,8 @@ if (!function_exists('remove_interact_as_actor')) {
 if (!function_exists('interact_as_actor_id')) {
     /**
      * Sets the podcast id of which the user is acting as
-     *
-     * @return integer
      */
-    function interact_as_actor_id()
+    function interact_as_actor_id(): int
     {
         $authenticate = Services::authentication();
         $authenticate->check();
@@ -58,7 +53,7 @@ if (!function_exists('interact_as_actor')) {
     /**
      * Get the actor the user is currently interacting as
      *
-     * @return \ActivityPub\Entities\Actor|false
+     * @return Actor|false
      */
     function interact_as_actor()
     {
@@ -78,11 +73,10 @@ if (!function_exists('interact_as_actor')) {
 
 if (!function_exists('can_user_interact')) {
     /**
-     * @return bool
      * @throws DataException
      */
-    function can_user_interact()
+    function can_user_interact(): bool
     {
-        return interact_as_actor() ? true : false;
+        return (bool) interact_as_actor();
     }
 }
diff --git a/app/Helpers/breadcrumb_helper.php b/app/Helpers/breadcrumb_helper.php
index 03d9c86b3a..503ee07711 100644
--- a/app/Helpers/breadcrumb_helper.php
+++ b/app/Helpers/breadcrumb_helper.php
@@ -8,20 +8,24 @@
 
 use Config\Services;
 
-/**
- * Renders the breadcrumb navigation through the Breadcrumb service
- *
- * @param  string $class to be added to the breadcrumb nav
- * @return string html breadcrumb
- */
-function render_breadcrumb($class = null)
-{
-    $breadcrumb = Services::breadcrumb();
-    return $breadcrumb->render($class);
+if (!function_exists('render_breadcrumb')) {
+    /**
+     * Renders the breadcrumb navigation through the Breadcrumb service
+     *
+     * @param  string $class to be added to the breadcrumb nav
+     * @return string html breadcrumb
+     */
+    function render_breadcrumb(string $class = null): string
+    {
+        $breadcrumb = Services::breadcrumb();
+        return $breadcrumb->render($class);
+    }
 }
 
-function replace_breadcrumb_params($newParams)
-{
-    $breadcrumb = Services::breadcrumb();
-    $breadcrumb->replaceParams($newParams);
+if (!function_exists('replace_breadcrumb_params')) {
+    function replace_breadcrumb_params($newParams): void
+    {
+        $breadcrumb = Services::breadcrumb();
+        $breadcrumb->replaceParams($newParams);
+    }
 }
diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php
index 7c85e95136..6b8f96c4bc 100644
--- a/app/Helpers/components_helper.php
+++ b/app/Helpers/components_helper.php
@@ -6,24 +6,25 @@
  * @link       https://castopod.org/
  */
 
+use CodeIgniter\View\Table;
+use CodeIgniter\I18n\Time;
+
 if (!function_exists('button')) {
     /**
      * Button component
      *
      * Creates a stylized button or button like anchor tag if the URL is defined.
      *
-     * @param string           $label The button label
-     * @param mixed|null       $uri URI string or array of URI segments
-     * @param array            $customOptions button options: variant, size, iconLeft, iconRight
-     * @param array            $customAttributes Additional attributes
+     * @param array  $customOptions button options: variant, size, iconLeft, iconRight
+     * @param array  $customAttributes Additional attributes
      *
      * @return string
      */
     function button(
         string $label = '',
-        $uri = null,
-        $customOptions = [],
-        $customAttributes = []
+        string $uri = '',
+        array $customOptions = [],
+        array $customAttributes = []
     ): string {
         $defaultOptions = [
             'variant' => 'default',
@@ -90,7 +91,7 @@ if (!function_exists('button')) {
             $label .= icon($options['iconRight'], 'ml-2');
         }
 
-        if ($uri) {
+        if ($uri !== '') {
             return anchor(
                 $uri,
                 $label,
@@ -111,8 +112,8 @@ if (!function_exists('button')) {
         );
 
         return <<<HTML
-            <button class="$buttonClass" $attributes>
-            $label
+            <button class="{$buttonClass}" {$attributes}>
+            {$label}
             </button>
         HTML;
     }
@@ -126,19 +127,19 @@ if (!function_exists('icon_button')) {
      *
      * Abstracts the `button()` helper to create a stylized icon button
      *
-     * @param string           $label The button label
-     * @param mixed|null       $uri URI string or array of URI segments
-     * @param array            $customOptions button options: variant, size, iconLeft, iconRight
-     * @param array            $customAttributes Additional attributes
+     * @param string $label The button label
+     * @param string  $uri URI string or array of URI segments
+     * @param array  $customOptions button options: variant, size, iconLeft, iconRight
+     * @param array  $customAttributes Additional attributes
      *
      * @return string
      */
     function icon_button(
         string $icon,
         string $title,
-        $uri = null,
-        $customOptions = [],
-        $customAttributes = []
+        string $uri = '',
+        array $customOptions = [],
+        array $customAttributes = []
     ): string {
         $defaultOptions = [
             'isSquared' => true,
@@ -197,9 +198,9 @@ if (!function_exists('data_table')) {
      *
      * @return string
      */
-    function data_table($columns, $data = [], ...$rest): string
+    function data_table(array $columns, array $data = [], ...$rest): string
     {
-        $table = new \CodeIgniter\View\Table();
+        $table = new Table();
 
         $template = [
             'table_open' => '<table class="w-full whitespace-no-wrap">',
@@ -219,17 +220,17 @@ if (!function_exists('data_table')) {
 
         $tableHeaders = [];
         foreach ($columns as $column) {
-            array_push($tableHeaders, $column['header']);
+            $tableHeaders[] = $column['header'];
         }
 
         $table->setHeading($tableHeaders);
 
-        if ($dataCount = count($data)) {
-            for ($i = 0; $i < $dataCount; $i++) {
+        if (($dataCount = count($data)) !== 0) {
+            for ($i = 0; $i < $dataCount; ++$i) {
                 $row = $data[$i];
                 $rowData = [];
                 foreach ($columns as $column) {
-                    array_push($rowData, $column['cell']($row, ...$rest));
+                    $rowData[] = $column['cell']($row, ...$rest);
                 }
                 $table->addRow($rowData);
             }
@@ -251,38 +252,39 @@ if (!function_exists('publication_pill')) {
      *
      * Shows the stylized publication datetime in regards to current datetime.
      *
-     * @param \CodeIgniter\I18n\Time    $publicationDate publication datetime of the episode
+     * @param Time $publicationDate publication datetime of the episode
      * @param boolean                   $isPublished whether or not the episode has been published
      * @param string                   $customClass css class to add to the component
      *
      * @return string
      */
     function publication_pill(
-        $publicationDate,
+        ?Time $publicationDate,
         $publicationStatus,
-        $customClass = ''
+        string $customClass = ''
     ): string {
+        if ($publicationDate === null) {
+            return '';
+        }
+
         $class =
             $publicationStatus === 'published'
                 ? 'text-pine-500 border-pine-500'
                 : 'text-red-600 border-red-600';
 
-        $transParam = [];
-        if ($publicationDate) {
-            $transParam = [
-                '<time pubdate datetime="' .
-                $publicationDate->format(DateTime::ATOM) .
-                '" title="' .
-                $publicationDate .
-                '">' .
-                lang('Common.mediumDate', [$publicationDate]) .
-                '</time>',
-            ];
-        }
+        $langOptions = [
+            '<time pubdate datetime="' .
+            $publicationDate->format(DateTime::ATOM) .
+            '" title="' .
+            $publicationDate .
+            '">' .
+            lang('Common.mediumDate', [$publicationDate]) .
+            '</time>',
+        ];
 
         $label = lang(
             'Episode.publication_status.' . $publicationStatus,
-            $transParam,
+            $langOptions,
         );
 
         return '<span class="px-1 font-semibold border ' .
@@ -303,15 +305,13 @@ if (!function_exists('publication_button')) {
      *
      * Displays the appropriate publication button depending on the publication status.
      *
-     * @param integer   $podcastId
-     * @param integer   $episodeId
      * @param boolean   $publicationStatus the episode's publication status     *
      * @return string
      */
     function publication_button(
-        $podcastId,
-        $episodeId,
-        $publicationStatus
+        int $podcastId,
+        int $episodeId,
+        bool $publicationStatus
     ): string {
         switch ($publicationStatus) {
             case 'not_published':
@@ -351,17 +351,15 @@ if (!function_exists('episode_numbering')) {
     /**
      * Returns relevant translated episode numbering.
      *
-     * @param int|null  $episodeNumber
-     * @param int|null  $seasonNumber
      * @param string    $class styling classes
      * @param string    $is_abbr component will show abbreviated numbering if true
      *
      * @return string|null
      */
     function episode_numbering(
-        $episodeNumber = null,
-        $seasonNumber = null,
-        $class = '',
+        ?int $episodeNumber = null,
+        ?int $seasonNumber = null,
+        string $class = '',
         $isAbbr = false
     ): string {
         if (!$episodeNumber && !$seasonNumber) {
@@ -409,36 +407,28 @@ if (!function_exists('episode_numbering')) {
 if (!function_exists('location_link')) {
     /**
      * Returns link to display from location info
-     *
-     * @param string $locationName
-     * @param string $locationGeo
-     * @param string $locationOsmid
-     *
-     * @return string
      */
     function location_link(
-        $locationName,
-        $locationGeo,
-        $locationOsmid,
+        ?string $locationName,
+        ?string $locationGeo,
+        ?string $locationOsmid,
         $class = ''
-    ) {
-        $link = '';
-
-        if (!empty($locationName)) {
-            $link = anchor(
-                location_url($locationName, $locationGeo, $locationOsmid),
-                icon('map-pin', 'mr-2') . $locationName,
-                [
-                    'class' =>
-                        'inline-flex items-baseline hover:underline' .
-                        (empty($class) ? '' : " $class"),
-                    'target' => '_blank',
-                    'rel' => 'noreferrer noopener',
-                ],
-            );
+    ): string {
+        if (empty($locationName)) {
+            return '';
         }
 
-        return $link;
+        return anchor(
+            location_url($locationName, $locationGeo, $locationOsmid),
+            icon('map-pin', 'mr-2') . $locationName,
+            [
+                'class' =>
+                    'inline-flex items-baseline hover:underline' .
+                    (empty($class) ? '' : " {$class}"),
+                'target' => '_blank',
+                'rel' => 'noreferrer noopener',
+            ],
+        );
     }
 }
 
diff --git a/app/Helpers/form_helper.php b/app/Helpers/form_helper.php
index 8e193cfbe2..afa928f283 100644
--- a/app/Helpers/form_helper.php
+++ b/app/Helpers/form_helper.php
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * @copyright  2020 Podlibre
  * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
@@ -71,20 +72,15 @@ if (!function_exists('form_switch')) {
      *
      * Abstracts form_label to stylize it as a switch toggle
      *
-     * @param array   $data
-     * @param string  $value
-     * @param boolean $checked
-     * @param mixed   $extra
-     *
      * @return string
      */
     function form_switch(
         $label = '',
-        $data = '',
+        array $data = [],
         string $value = '',
         bool $checked = false,
-        $class = '',
-        $extra = ''
+        string $class = '',
+        array $extra = []
     ): string {
         $data['class'] = 'form-switch';
 
@@ -155,31 +151,26 @@ if (!function_exists('form_multiselect')) {
     /**
      * Multi-select menu
      *
-     * @param string $name
-     * @param array  $options
-     * @param array  $selected
-     * @param mixed  $extra
-     *
      * @return string
      */
     function form_multiselect(
         string $name = '',
         array $options = [],
         array $selected = [],
-        $customExtra = ''
+        array $customExtra = []
     ): string {
         $defaultExtra = [
             'data-class' => $customExtra['class'],
             'data-select-text' => lang('Common.forms.multiSelect.selectText'),
             'data-loading-text' => lang('Common.forms.multiSelect.loadingText'),
             'data-no-results-text' => lang(
-                'Common.forms.multiSelect.noResultsText'
+                'Common.forms.multiSelect.noResultsText',
             ),
             'data-no-choices-text' => lang(
-                'Common.forms.multiSelect.noChoicesText'
+                'Common.forms.multiSelect.noChoicesText',
             ),
             'data-max-item-text' => lang(
-                'Common.forms.multiSelect.maxItemText'
+                'Common.forms.multiSelect.maxItemText',
             ),
         ];
         $extra = stringify_attributes(array_merge($defaultExtra, $customExtra));
diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php
index ab0a567cf7..06c2af7d71 100644
--- a/app/Helpers/id3_helper.php
+++ b/app/Helpers/id3_helper.php
@@ -6,108 +6,112 @@
  * @link       https://castopod.org/
  */
 
+use App\Entities\Episode;
+use CodeIgniter\Files\File;
 use JamesHeinrich\GetID3\GetID3;
 use JamesHeinrich\GetID3\WriteTags;
 
-/**
- * Gets audio file metadata and ID3 info
- *
- * @param UploadedFile $file
- *
- * @return array
- */
-function get_file_tags($file)
-{
-    $getID3 = new GetID3();
-    $FileInfo = $getID3->analyze($file);
-
-    return [
-        'filesize' => $FileInfo['filesize'],
-        'mime_type' => $FileInfo['mime_type'],
-        'avdataoffset' => $FileInfo['avdataoffset'],
-        'playtime_seconds' => $FileInfo['playtime_seconds'],
-    ];
+if (!function_exists('get_file_tags')) {
+    /**
+     * Gets audio file metadata and ID3 info
+     *
+     * @param UploadedFile $file
+     */
+    function get_file_tags($file): array
+    {
+        $getID3 = new GetID3();
+        $FileInfo = $getID3->analyze($file);
+
+        return [
+            'filesize' => $FileInfo['filesize'],
+            'mime_type' => $FileInfo['mime_type'],
+            'avdataoffset' => $FileInfo['avdataoffset'],
+            'playtime_seconds' => $FileInfo['playtime_seconds'],
+        ];
+    }
 }
 
-/**
- * Write audio file metadata / ID3 tags
- *
- * @param App\Entities\Episode $episode
- *
- * @return UploadedFile
- */
-function write_audio_file_tags($episode)
-{
-    helper('media');
-
-    $TextEncoding = 'UTF-8';
-
-    // Initialize getID3 tag-writing module
-    $tagwriter = new WriteTags();
-    $tagwriter->filename = media_path($episode->audio_file_path);
-
-    // set various options (optional)
-    $tagwriter->tagformats = ['id3v2.4'];
-    $tagwriter->tag_encoding = $TextEncoding;
-
-    $cover = new \CodeIgniter\Files\File($episode->image->id3_path);
-
-    $APICdata = file_get_contents($cover->getRealPath());
-
-    // TODO: variables used for podcast specific tags
-    // $podcast_url = $episode->podcast->link;
-    // $podcast_feed_url = $episode->podcast->feed_url;
-    // $episode_media_url = $episode->link;
-
-    // populate data array
-    $TagData = [
-        'title' => [$episode->title],
-        'artist' => [
-            empty($episode->podcast->publisher)
-                ? $episode->podcast->owner_name
-                : $episode->podcast->publisher,
-        ],
-        'album' => [$episode->podcast->title],
-        'year' => [
-            $episode->published_at ? $episode->published_at->format('Y') : '',
-        ],
-        'genre' => ['Podcast'],
-        'comment' => [$episode->description],
-        'track_number' => [strval($episode->number)],
-        'copyright_message' => [$episode->podcast->copyright],
-        'publisher' => [
-            empty($episode->podcast->publisher)
-                ? $episode->podcast->owner_name
-                : $episode->podcast->publisher,
-        ],
-        'encoded_by' => ['Castopod'],
-
-        // TODO: find a way to add the remaining tags for podcasts as the library doesn't seem to allow it
-        // 'website' => [$podcast_url],
-        // 'podcast' => [],
-        // 'podcast_identifier' => [$episode_media_url],
-        // 'podcast_feed' => [$podcast_feed_url],
-        // 'podcast_description' => [$podcast->description_markdown],
-    ];
-
-    $TagData['attached_picture'][] = [
-        'picturetypeid' => 2, // Cover. More: module.tag.id3v2.php
-        'data' => $APICdata,
-        'description' => 'cover',
-        'mime' => $cover->getMimeType(),
-    ];
-
-    $tagwriter->tag_data = $TagData;
-
-    // write tags
-    if ($tagwriter->WriteTags()) {
-        echo 'Successfully wrote tags<br>';
-        if (!empty($tagwriter->warnings)) {
-            echo 'There were some warnings:<br>' .
-                implode('<br><br>', $tagwriter->warnings);
+if (!function_exists('write_audio_file_tags')) {
+    /**
+     * Write audio file metadata / ID3 tags
+     *
+     * @return UploadedFile
+     */
+    function write_audio_file_tags(Episode $episode): void
+    {
+        helper('media');
+
+        $TextEncoding = 'UTF-8';
+
+        // Initialize getID3 tag-writing module
+        $tagwriter = new WriteTags();
+        $tagwriter->filename = media_path($episode->audio_file_path);
+
+        // set various options (optional)
+        $tagwriter->tagformats = ['id3v2.4'];
+        $tagwriter->tag_encoding = $TextEncoding;
+
+        $cover = new File($episode->image->id3_path);
+
+        $APICdata = file_get_contents($cover->getRealPath());
+
+        // TODO: variables used for podcast specific tags
+        // $podcast_url = $episode->podcast->link;
+        // $podcast_feed_url = $episode->podcast->feed_url;
+        // $episode_media_url = $episode->link;
+
+        // populate data array
+        $TagData = [
+            'title' => [$episode->title],
+            'artist' => [
+                empty($episode->podcast->publisher)
+                    ? $episode->podcast->owner_name
+                    : $episode->podcast->publisher,
+            ],
+            'album' => [$episode->podcast->title],
+            'year' => [
+                $episode->published_at
+                    ? $episode->published_at->format('Y')
+                    : '',
+            ],
+            'genre' => ['Podcast'],
+            'comment' => [$episode->description],
+            'track_number' => [strval($episode->number)],
+            'copyright_message' => [$episode->podcast->copyright],
+            'publisher' => [
+                empty($episode->podcast->publisher)
+                    ? $episode->podcast->owner_name
+                    : $episode->podcast->publisher,
+            ],
+            'encoded_by' => ['Castopod'],
+
+            // TODO: find a way to add the remaining tags for podcasts as the library doesn't seem to allow it
+            // 'website' => [$podcast_url],
+            // 'podcast' => [],
+            // 'podcast_identifier' => [$episode_media_url],
+            // 'podcast_feed' => [$podcast_feed_url],
+            // 'podcast_description' => [$podcast->description_markdown],
+        ];
+
+        $TagData['attached_picture'][] = [
+            'picturetypeid' => 2, // Cover. More: module.tag.id3v2.php
+            'data' => $APICdata,
+            'description' => 'cover',
+            'mime' => $cover->getMimeType(),
+        ];
+
+        $tagwriter->tag_data = $TagData;
+
+        // write tags
+        if ($tagwriter->WriteTags()) {
+            echo 'Successfully wrote tags<br>';
+            if (!empty($tagwriter->warnings)) {
+                echo 'There were some warnings:<br>' .
+                    implode('<br><br>', $tagwriter->warnings);
+            }
+        } else {
+            echo 'Failed to write tags!<br>' .
+                implode('<br><br>', $tagwriter->errors);
         }
-    } else {
-        echo 'Failed to write tags!<br>' .
-            implode('<br><br>', $tagwriter->errors);
     }
 }
diff --git a/app/Helpers/location_helper.php b/app/Helpers/location_helper.php
index 4b4207c383..11a0498d20 100644
--- a/app/Helpers/location_helper.php
+++ b/app/Helpers/location_helper.php
@@ -6,47 +6,56 @@
  * @link       https://castopod.org/
  */
 
-/**
- * Fetches places from Nominatim OpenStreetMap
- *
- * @param string $locationName
- *
- * @return array|null
- */
-function fetch_osm_location($locationName)
-{
-    $osmObject = null;
-    if (!empty($locationName)) {
-        try {
-            $client = \Config\Services::curlrequest();
+use Config\Services;
+
+if (!function_exists('fetch_osm_location')) {
+    /**
+     * Fetches places from Nominatim OpenStreetMap
+     *
+     * @return array|null
+     */
+    function fetch_osm_location(string $locationName): ?array
+    {
+        $osmObject = null;
 
-            $response = $client->request(
-                'GET',
-                'https://nominatim.openstreetmap.org/search.php?q=' .
-                    urlencode($locationName) .
-                    '&polygon_geojson=1&format=jsonv2',
-                [
-                    'headers' => [
-                        'User-Agent' => 'Castopod/' . CP_VERSION,
-                        'Accept' => 'application/json',
+        if (!empty($locationName)) {
+            try {
+                $client = Services::curlrequest();
+
+                $response = $client->request(
+                    'GET',
+                    'https://nominatim.openstreetmap.org/search.php?q=' .
+                        urlencode($locationName) .
+                        '&polygon_geojson=1&format=jsonv2',
+                    [
+                        'headers' => [
+                            'User-Agent' => 'Castopod/' . CP_VERSION,
+                            'Accept' => 'application/json',
+                        ],
                     ],
-                ]
-            );
-            $places = json_decode($response->getBody(), true);
-            $osmObject = [
-                'geo' =>
-                    empty($places[0]['lat']) || empty($places[0]['lon'])
+                );
+                $places = json_decode(
+                    $response->getBody(),
+                    true,
+                    512,
+                    JSON_THROW_ON_ERROR,
+                );
+                $osmObject = [
+                    'geo' =>
+                        empty($places[0]['lat']) || empty($places[0]['lon'])
+                            ? null
+                            : "geo:{$places[0]['lat']},{$places[0]['lon']}",
+                    'osmid' => empty($places[0]['osm_type'])
                         ? null
-                        : "geo:{$places[0]['lat']},{$places[0]['lon']}",
-                'osmid' => empty($places[0]['osm_type'])
-                    ? null
-                    : strtoupper(substr($places[0]['osm_type'], 0, 1)) .
-                        $places[0]['osm_id'],
-            ];
-        } catch (\Exception $e) {
-            //If things go wrong the show must go on
-            log_message('critical', $e);
+                        : strtoupper(substr($places[0]['osm_type'], 0, 1)) .
+                            $places[0]['osm_id'],
+                ];
+            } catch (Exception $exception) {
+                //If things go wrong the show must go on
+                log_message('critical', $exception);
+            }
         }
+
+        return $osmObject;
     }
-    return $osmObject;
 }
diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php
index cd71e3e608..7074bcb737 100644
--- a/app/Helpers/media_helper.php
+++ b/app/Helpers/media_helper.php
@@ -8,125 +8,120 @@
 
 use CodeIgniter\Files\File;
 use CodeIgniter\HTTP\ResponseInterface;
-
-/**
- * Saves a file to the corresponding podcast folder in `public/media`
- *
- * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $filePath
- * @param string $folder
- * @param string $fileName
- *
- * @return string The episode's file path in media root
- */
-function save_media($filePath, $folder, $mediaName)
-{
-    $fileName = $mediaName . '.' . $filePath->getExtension();
-
-    $mediaRoot = config('App')->mediaRoot . '/' . $folder;
-
-    if (!file_exists($mediaRoot)) {
-        mkdir($mediaRoot, 0777, true);
-        touch($mediaRoot . '/index.html');
+use CodeIgniter\HTTP\Files\UploadedFile;
+use Config\Services;
+
+if (!function_exists('save_media')) {
+    /**
+     * Saves a file to the corresponding podcast folder in `public/media`
+     *
+     * @param File|UploadedFile $filePath
+     */
+    function save_media(
+        File $filePath,
+        string $folder,
+        string $mediaName
+    ): string {
+        $fileName = $mediaName . '.' . $filePath->getExtension();
+
+        $mediaRoot = config('App')->mediaRoot . '/' . $folder;
+
+        if (!file_exists($mediaRoot)) {
+            mkdir($mediaRoot, 0777, true);
+            touch($mediaRoot . '/index.html');
+        }
+
+        // move to media folder and overwrite file if already existing
+        $filePath->move($mediaRoot . '/', $fileName, true);
+
+        return $folder . '/' . $fileName;
     }
-
-    // move to media folder and overwrite file if already existing
-    $filePath->move($mediaRoot . '/', $fileName, true);
-
-    return $folder . '/' . $fileName;
 }
 
-/**
- * @param string $fileUrl
- * @return File
- */
-function download_file($fileUrl)
-{
-    $client = \Config\Services::curlrequest();
+if (!function_exists('download_file')) {
+    function download_file(string $fileUrl): File
+    {
+        $client = Services::curlrequest();
 
-    $response = $client->get($fileUrl, [
-        'headers' => [
-            'User-Agent' => 'Castopod/' . CP_VERSION,
-        ],
-    ]);
-
-    // redirect to new file location
-    $newFileUrl = $fileUrl;
-    while (
-        in_array(
-            $response->getStatusCode(),
-            [
-                ResponseInterface::HTTP_MOVED_PERMANENTLY,
-                ResponseInterface::HTTP_FOUND,
-                ResponseInterface::HTTP_SEE_OTHER,
-                ResponseInterface::HTTP_NOT_MODIFIED,
-                ResponseInterface::HTTP_TEMPORARY_REDIRECT,
-                ResponseInterface::HTTP_PERMANENT_REDIRECT,
-            ],
-            true,
-        )
-    ) {
-        $newFileUrl = (string) trim(
-            $response->getHeader('location')->getValue(),
-        );
-        $response = $client->get($newFileUrl, [
+        $response = $client->get($fileUrl, [
             'headers' => [
                 'User-Agent' => 'Castopod/' . CP_VERSION,
             ],
-            'http_errors' => false,
         ]);
-    }
-    $tmpFilename =
-        time() .
-        '_' .
-        bin2hex(random_bytes(10)) .
-        '.' .
-        pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
-    $tmpFilePath = WRITEPATH . 'uploads/' . $tmpFilename;
-    file_put_contents($tmpFilePath, $response->getBody());
-
-    return new \CodeIgniter\Files\File($tmpFilePath);
-}
 
-/**
- * Prefixes the root media path to a given uri
- *
- * @param  mixed  $uri      URI string or array of URI segments
- * @return string
- */
-function media_path($uri = ''): string
-{
-    // convert segment array to string
-    if (is_array($uri)) {
-        $uri = implode('/', $uri);
+        // redirect to new file location
+        $newFileUrl = $fileUrl;
+        while (
+            in_array(
+                $response->getStatusCode(),
+                [
+                    ResponseInterface::HTTP_MOVED_PERMANENTLY,
+                    ResponseInterface::HTTP_FOUND,
+                    ResponseInterface::HTTP_SEE_OTHER,
+                    ResponseInterface::HTTP_NOT_MODIFIED,
+                    ResponseInterface::HTTP_TEMPORARY_REDIRECT,
+                    ResponseInterface::HTTP_PERMANENT_REDIRECT,
+                ],
+                true,
+            )
+        ) {
+            $newFileUrl = trim($response->getHeader('location')->getValue());
+            $response = $client->get($newFileUrl, [
+                'headers' => [
+                    'User-Agent' => 'Castopod/' . CP_VERSION,
+                ],
+                'http_errors' => false,
+            ]);
+        }
+        $tmpFilename =
+            time() .
+            '_' .
+            bin2hex(random_bytes(10)) .
+            '.' .
+            pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
+        $tmpFilePath = WRITEPATH . 'uploads/' . $tmpFilename;
+        file_put_contents($tmpFilePath, $response->getBody());
+
+        return new File($tmpFilePath);
     }
-    $uri = trim($uri, '/');
-
-    return config('App')->mediaRoot . '/' . $uri;
 }
-
-/**
- * Return the media base URL to use in views
- *
- * @param  mixed  $uri      URI string or array of URI segments
- * @param  string $protocol
- * @return string
- */
-function media_url($uri = '', string $protocol = null): string
-{
-    return base_url(config('App')->mediaRoot . '/' . $uri, $protocol);
+if (!function_exists('media_path')) {
+    /**
+     * Prefixes the root media path to a given uri
+     *
+     * @param  string|array  $uri URI string or array of URI segments
+     */
+    function media_path($uri = ''): string
+    {
+        // convert segment array to string
+        if (is_array($uri)) {
+            $uri = implode('/', $uri);
+        }
+        $uri = trim($uri, '/');
+
+        return config('App')->mediaRoot . '/' . $uri;
+    }
 }
 
-function media_base_url($uri = '')
-{
-    // convert segment array to string
-    if (is_array($uri)) {
-        $uri = implode('/', $uri);
+if (!function_exists('media_base_url')) {
+    /**
+     * Return the media base URL to use in views
+     *
+     * @param  string|array $uri      URI string or array of URI segments
+     * @param  string $protocol
+     */
+    function media_base_url($uri = ''): string
+    {
+        // convert segment array to string
+        if (is_array($uri)) {
+            $uri = implode('/', $uri);
+        }
+        $uri = trim($uri, '/');
+
+        return rtrim(config('App')->mediaBaseURL, '/') .
+            '/' .
+            config('App')->mediaRoot .
+            '/' .
+            $uri;
     }
-    $uri = trim($uri, '/');
-
-    return rtrim(config('App')->mediaBaseURL, '/') .
-        '/' .
-        config('App')->mediaRoot .
-        '/' .
-        $uri;
 }
diff --git a/app/Helpers/misc_helper.php b/app/Helpers/misc_helper.php
index 92c16ffa44..2eea097263 100644
--- a/app/Helpers/misc_helper.php
+++ b/app/Helpers/misc_helper.php
@@ -6,142 +6,140 @@
  * @link       https://castopod.org/
  */
 
-/**
- * Gets the browser default language using the request header key `HTTP_ACCEPT_LANGUAGE`
- *
- * @param mixed $http_accept_language
- *
- * @return string|null ISO 639-1 language code or null
- */
-function get_browser_language($http_accept_language)
-{
-    $langs = explode(',', $http_accept_language);
-    if (!empty($langs)) {
-        return substr($langs[0], 0, 2);
-    }
+if (!function_exists('get_browser_language')) {
+    /**
+     * Gets the browser default language using the request header key `HTTP_ACCEPT_LANGUAGE`
+     *
+     * @return string|null ISO 639-1 language code or null
+     */
+    function get_browser_language(string $httpAcceptLanguage): ?string
+    {
+        $langs = explode(',', $httpAcceptLanguage);
+        if (!empty($langs)) {
+            return substr($langs[0], 0, 2);
+        }
 
-    return null;
+        return null;
+    }
 }
 
-/**
- * Check if a string starts with some characters
- *
- * @param string $string
- * @param string $query
- *
- * @return bool
- */
-function startsWith($string, $query)
-{
-    return substr($string, 0, strlen($query)) === $query;
+if (!function_exists('startsWith')) {
+    /**
+     * Check if a string starts with some characters
+     */
+    function startsWith(string $string, string $query): bool
+    {
+        return substr($string, 0, strlen($query)) === $query;
+    }
 }
 
-function slugify($text)
-{
-    if (empty($text)) {
-        return 'n-a';
+if (!function_exists('slugify')) {
+    function slugify($text)
+    {
+        if (empty($text)) {
+            return 'n-a';
+        }
+
+        // replace non letter or digits by -
+        $text = preg_replace('~[^\pL\d]+~u', '-', $text);
+
+        $unwanted_array = [
+            'Å ' => 'S',
+            'Å¡' => 's',
+            'Đ' => 'Dj',
+            'Ä‘' => 'dj',
+            'Ž' => 'Z',
+            'ž' => 'z',
+            'Č' => 'C',
+            'č' => 'c',
+            'Ć' => 'C',
+            'ć' => 'c',
+            'À' => 'A',
+            'Á' => 'A',
+            'Â' => 'A',
+            'Ã' => 'A',
+            'Ä' => 'A',
+            'Ã…' => 'A',
+            'Æ' => 'AE',
+            'Ç' => 'C',
+            'È' => 'E',
+            'É' => 'E',
+            'Ê' => 'E',
+            'Ë' => 'E',
+            'Ì' => 'I',
+            'Í' => 'I',
+            'ÃŽ' => 'I',
+            'Ï' => 'I',
+            'Ñ' => 'N',
+            'Ã’' => 'O',
+            'Ó' => 'O',
+            'Ô' => 'O',
+            'Õ' => 'O',
+            'Ö' => 'O',
+            'Ø' => 'O',
+            'Å’' => 'OE',
+            'Ù' => 'U',
+            'Ú' => 'U',
+            'Û' => 'U',
+            'Ü' => 'U',
+            'Ý' => 'Y',
+            'Þ' => 'B',
+            'ß' => 'Ss',
+            'à' => 'a',
+            'á' => 'a',
+            'â' => 'a',
+            'ã' => 'a',
+            'ä' => 'a',
+            'Ã¥' => 'a',
+            'æ' => 'ae',
+            'ç' => 'c',
+            'è' => 'e',
+            'é' => 'e',
+            'ê' => 'e',
+            'ë' => 'e',
+            'ì' => 'i',
+            'í' => 'i',
+            'î' => 'i',
+            'ï' => 'i',
+            'ð' => 'o',
+            'ñ' => 'n',
+            'ò' => 'o',
+            'ó' => 'o',
+            'ô' => 'o',
+            'õ' => 'o',
+            'ö' => 'o',
+            'ø' => 'o',
+            'Å“' => 'OE',
+            'ù' => 'u',
+            'ú' => 'u',
+            'û' => 'u',
+            'ý' => 'y',
+            'þ' => 'b',
+            'ÿ' => 'y',
+            'Å”' => 'R',
+            'Å•' => 'r',
+            '/' => '-',
+            ' ' => '-',
+        ];
+        $text = strtr($text, $unwanted_array);
+
+        // transliterate
+        $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
+
+        // remove unwanted characters
+        $text = preg_replace('~[^\\-\w]+~', '', $text);
+
+        // trim
+        $text = trim($text, '-');
+
+        // remove duplicate -
+        $text = preg_replace('~-+~', '-', $text);
+
+        // lowercase
+        $text = strtolower($text);
+
+        return $text;
     }
-
-    // replace non letter or digits by -
-    $text = preg_replace('~[^\pL\d]+~u', '-', $text);
-
-    $unwanted_array = [
-        'Å ' => 'S',
-        'Å¡' => 's',
-        'Đ' => 'Dj',
-        'Ä‘' => 'dj',
-        'Ž' => 'Z',
-        'ž' => 'z',
-        'Č' => 'C',
-        'č' => 'c',
-        'Ć' => 'C',
-        'ć' => 'c',
-        'À' => 'A',
-        'Á' => 'A',
-        'Â' => 'A',
-        'Ã' => 'A',
-        'Ä' => 'A',
-        'Ã…' => 'A',
-        'Æ' => 'AE',
-        'Ç' => 'C',
-        'È' => 'E',
-        'É' => 'E',
-        'Ê' => 'E',
-        'Ë' => 'E',
-        'Ì' => 'I',
-        'Í' => 'I',
-        'ÃŽ' => 'I',
-        'Ï' => 'I',
-        'Ñ' => 'N',
-        'Ã’' => 'O',
-        'Ó' => 'O',
-        'Ô' => 'O',
-        'Õ' => 'O',
-        'Ö' => 'O',
-        'Ø' => 'O',
-        'Å’' => 'OE',
-        'Ù' => 'U',
-        'Ú' => 'U',
-        'Û' => 'U',
-        'Ü' => 'U',
-        'Ý' => 'Y',
-        'Þ' => 'B',
-        'ß' => 'Ss',
-        'à' => 'a',
-        'á' => 'a',
-        'â' => 'a',
-        'ã' => 'a',
-        'ä' => 'a',
-        'Ã¥' => 'a',
-        'æ' => 'ae',
-        'ç' => 'c',
-        'è' => 'e',
-        'é' => 'e',
-        'ê' => 'e',
-        'ë' => 'e',
-        'ì' => 'i',
-        'í' => 'i',
-        'î' => 'i',
-        'ï' => 'i',
-        'ð' => 'o',
-        'ñ' => 'n',
-        'ò' => 'o',
-        'ó' => 'o',
-        'ô' => 'o',
-        'õ' => 'o',
-        'ö' => 'o',
-        'ø' => 'o',
-        'Å“' => 'OE',
-        'ù' => 'u',
-        'ú' => 'u',
-        'û' => 'u',
-        'ý' => 'y',
-        'ý' => 'y',
-        'þ' => 'b',
-        'ÿ' => 'y',
-        'Å”' => 'R',
-        'Å•' => 'r',
-        '/' => '-',
-        ' ' => '-',
-    ];
-    $text = strtr($text, $unwanted_array);
-
-    // transliterate
-    $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
-
-    // remove unwanted characters
-    $text = preg_replace('~[^-\w]+~', '', $text);
-
-    // trim
-    $text = trim($text, '-');
-
-    // remove duplicate -
-    $text = preg_replace('~-+~', '-', $text);
-
-    // lowercase
-    $text = strtolower($text);
-
-    return $text;
 }
 
 //--------------------------------------------------------------------
@@ -151,11 +149,8 @@ if (!function_exists('format_duration')) {
      * Formats duration in seconds to an hh:mm:ss string
      *
      * @param int $seconds seconds to format
-     * @param string $separator
-     *
-     * @return string
      */
-    function format_duration($seconds, $separator = ':')
+    function format_duration(int $seconds, string $separator = ':'): string
     {
         return sprintf(
             '%02d%s%02d%s%02d',
@@ -163,7 +158,7 @@ if (!function_exists('format_duration')) {
             $separator,
             ($seconds / 60) % 60,
             $separator,
-            $seconds % 60
+            $seconds % 60,
         );
     }
 }
diff --git a/app/Helpers/page_helper.php b/app/Helpers/page_helper.php
index 5dda5b387a..ebdf4a28a2 100644
--- a/app/Helpers/page_helper.php
+++ b/app/Helpers/page_helper.php
@@ -8,26 +8,27 @@
 
 use App\Models\PageModel;
 
-/**
- * Returns instance pages as links inside nav tag
- *
- * @param string $class
- * @return string html pages navigation
- */
-function render_page_links($class = null)
-{
-    $pages = (new PageModel())->findAll();
-    $links = anchor(route_to('home'), lang('Common.home'), [
-        'class' => 'px-2 underline hover:no-underline',
-    ]);
-    $links .= anchor(route_to('credits'), lang('Person.credits'), [
-        'class' => 'px-2 underline hover:no-underline',
-    ]);
-    foreach ($pages as $page) {
-        $links .= anchor($page->link, $page->title, [
+if (!function_exists('render_page_links')) {
+    /**
+     * Returns instance pages as links inside nav tag
+     *
+     * @return string html pages navigation
+     */
+    function render_page_links(string $class = null): string
+    {
+        $pages = (new PageModel())->findAll();
+        $links = anchor(route_to('home'), lang('Common.home'), [
             'class' => 'px-2 underline hover:no-underline',
         ]);
-    }
+        $links .= anchor(route_to('credits'), lang('Person.credits'), [
+            'class' => 'px-2 underline hover:no-underline',
+        ]);
+        foreach ($pages as $page) {
+            $links .= anchor($page->link, $page->title, [
+                'class' => 'px-2 underline hover:no-underline',
+            ]);
+        }
 
-    return '<nav class="' . $class . '">' . $links . '</nav>';
+        return '<nav class="' . $class . '">' . $links . '</nav>';
+    }
 }
diff --git a/app/Helpers/persons_helper.php b/app/Helpers/persons_helper.php
index 62dd6b1264..29fb94617a 100644
--- a/app/Helpers/persons_helper.php
+++ b/app/Helpers/persons_helper.php
@@ -6,45 +6,47 @@
  * @link       https://castopod.org/
  */
 
-/**
- * Fetches persons from an episode
- *
- * @param array  $persons
- * @param array    &$personsArray
- */
-function construct_person_array($persons, &$personsArray)
-{
-    foreach ($persons as $person) {
-        if (array_key_exists($person->person->id, $personsArray)) {
-            $personsArray[$person->person->id]['roles'] .=
-                empty($person->person_group) || empty($person->person_role)
-                    ? ''
-                    : (empty($personsArray[$person->person->id]['roles'])
-                            ? ''
-                            : ', ') .
-                        lang(
-                            'PersonsTaxonomy.persons.' .
-                                $person->person_group .
-                                '.roles.' .
-                                $person->person_role .
-                                '.label',
-                        );
-        } else {
-            $personsArray[$person->person->id] = [
-                'full_name' => $person->person->full_name,
-                'information_url' => $person->person->information_url,
-                'thumbnail_url' => $person->person->image->thumbnail_url,
-                'roles' =>
+if (!function_exists('construct_person_array')) {
+    /**
+     * Fetches persons from an episode
+     *
+     * @param array    &$personsArray
+     */
+    function construct_person_array(array $persons, &$personsArray): void
+    {
+        foreach ($persons as $person) {
+            if (array_key_exists($person->person->id, $personsArray)) {
+                $personsArray[$person->person->id]['roles'] .=
                     empty($person->person_group) || empty($person->person_role)
                         ? ''
-                        : lang(
-                            'PersonsTaxonomy.persons.' .
-                                $person->person_group .
-                                '.roles.' .
-                                $person->person_role .
-                                '.label',
-                        ),
-            ];
+                        : (empty($personsArray[$person->person->id]['roles'])
+                                ? ''
+                                : ', ') .
+                            lang(
+                                'PersonsTaxonomy.persons.' .
+                                    $person->person_group .
+                                    '.roles.' .
+                                    $person->person_role .
+                                    '.label',
+                            );
+            } else {
+                $personsArray[$person->person->id] = [
+                    'full_name' => $person->person->full_name,
+                    'information_url' => $person->person->information_url,
+                    'thumbnail_url' => $person->person->image->thumbnail_url,
+                    'roles' =>
+                        empty($person->person_group) ||
+                        empty($person->person_role)
+                            ? ''
+                            : lang(
+                                'PersonsTaxonomy.persons.' .
+                                    $person->person_group .
+                                    '.roles.' .
+                                    $person->person_role .
+                                    '.label',
+                            ),
+                ];
+            }
         }
     }
 }
diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php
index a49562cc98..314f30aad4 100644
--- a/app/Helpers/rss_helper.php
+++ b/app/Helpers/rss_helper.php
@@ -9,517 +9,564 @@
 use App\Libraries\SimpleRSSElement;
 use CodeIgniter\I18n\Time;
 use Config\Mimes;
-
-/**
- * Generates the rss feed for a given podcast entity
- *
- * @param App\Entities\Podcast $podcast
- * @param string $service The name of the service that fetches the RSS feed for future reference when the audio file is eventually downloaded
- * @return string rss feed as xml
- */
-function get_rss_feed($podcast, $serviceSlug = '')
-{
-    $episodes = $podcast->episodes;
-
-    $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
-
-    $podcast_namespace =
-        'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md';
-
-    $rss = new SimpleRSSElement(
-        "<?xml version='1.0' encoding='utf-8'?><rss version='2.0' xmlns:itunes='$itunes_namespace' xmlns:podcast='$podcast_namespace' xmlns:content='http://purl.org/rss/1.0/modules/content/'></rss>",
-    );
-
-    $channel = $rss->addChild('channel');
-
-    $atom_link = $channel->addChild(
-        'atom:link',
-        null,
-        'http://www.w3.org/2005/Atom',
-    );
-    $atom_link->addAttribute('href', $podcast->feed_url);
-    $atom_link->addAttribute('rel', 'self');
-    $atom_link->addAttribute('type', 'application/rss+xml');
-
-    if (!empty($podcast->new_feed_url)) {
-        $channel->addChild(
-            'new-feed-url',
-            $podcast->new_feed_url,
-            $itunes_namespace,
+use App\Entities\Podcast;
+use App\Entities\Category;
+
+if (!function_exists('get_rss_feed')) {
+    /**
+     * Generates the rss feed for a given podcast entity
+     *
+     * @param string $service The name of the service that fetches the RSS feed for future reference when the audio file is eventually downloaded
+     * @return string rss feed as xml
+     */
+    function get_rss_feed(Podcast $podcast, $serviceSlug = ''): string
+    {
+        $episodes = $podcast->episodes;
+
+        $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
+
+        $podcast_namespace =
+            'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md';
+
+        $rss = new SimpleRSSElement(
+            "<?xml version='1.0' encoding='utf-8'?><rss version='2.0' xmlns:itunes='{$itunes_namespace}' xmlns:podcast='{$podcast_namespace}' xmlns:content='http://purl.org/rss/1.0/modules/content/'></rss>",
         );
-    }
 
-    // the last build date corresponds to the creation of the feed.xml cache
-    $channel->addChild(
-        'lastBuildDate',
-        (new Time('now'))->format(DATE_RFC1123),
-    );
-    $channel->addChild('generator', 'Castopod Host - https://castopod.org/');
-    $channel->addChild('docs', 'https://cyber.harvard.edu/rss/rss.html');
-
-    $channel->addChild('title', $podcast->title);
-    $channel->addChildWithCDATA('description', $podcast->description_html);
-    $itunes_image = $channel->addChild('image', null, $itunes_namespace);
-    $itunes_image->addAttribute('href', $podcast->image->original_url);
-    $channel->addChild('language', $podcast->language_code);
-    if (!empty($podcast->location_name)) {
-        $locationElement = $channel->addChild(
-            'location',
-            htmlspecialchars($podcast->location_name),
-            $podcast_namespace,
-        );
-        if (!empty($podcast->location_geo)) {
-            $locationElement->addAttribute('geo', $podcast->location_geo);
-        }
-        if (!empty($podcast->location_osmid)) {
-            $locationElement->addAttribute('osm', $podcast->location_osmid);
-        }
-    }
-    if (!empty($podcast->payment_pointer)) {
-        $valueElement = $channel->addChild('value', null, $podcast_namespace);
-        $valueElement->addAttribute('type', 'webmonetization');
-        $valueElement->addAttribute('method', '');
-        $valueElement->addAttribute('suggested', '');
-        $recipientElement = $valueElement->addChild(
-            'valueRecipient',
-            null,
-            $podcast_namespace,
-        );
-        $recipientElement->addAttribute('name', $podcast->owner_name);
-        $recipientElement->addAttribute('type', 'ILP');
-        $recipientElement->addAttribute('address', $podcast->payment_pointer);
-        $recipientElement->addAttribute('split', 100);
-    }
-    $channel
-        ->addChild(
-            'locked',
-            $podcast->is_locked ? 'yes' : 'no',
-            $podcast_namespace,
-        )
-        ->addAttribute('owner', $podcast->owner_email);
-    if (!empty($podcast->imported_feed_url)) {
-        $channel->addChild(
-            'previousUrl',
-            $podcast->imported_feed_url,
-            $podcast_namespace,
-        );
-    }
+        $channel = $rss->addChild('channel');
 
-    foreach ($podcast->podcastingPlatforms as $podcastingPlatform) {
-        $podcastingPlatformElement = $channel->addChild(
-            'id',
+        $atom_link = $channel->addChild(
+            'atom:link',
             null,
-            $podcast_namespace,
-        );
-        $podcastingPlatformElement->addAttribute(
-            'platform',
-            $podcastingPlatform->slug,
-        );
-        if (!empty($podcastingPlatform->link_content)) {
-            $podcastingPlatformElement->addAttribute(
-                'id',
-                $podcastingPlatform->link_content,
-            );
-        }
-        if (!empty($podcastingPlatform->link_url)) {
-            $podcastingPlatformElement->addAttribute(
-                'url',
-                htmlspecialchars($podcastingPlatform->link_url),
-            );
-        }
-    }
-
-    foreach ($podcast->socialPlatforms as $socialPlatform) {
-        $socialPlatformElement = $channel->addChild(
-            'social',
-            $socialPlatform->link_content,
-            $podcast_namespace,
-        );
-        $socialPlatformElement->addAttribute('platform', $socialPlatform->slug);
-        if (!empty($socialPlatform->link_url)) {
-            $socialPlatformElement->addAttribute(
-                'url',
-                htmlspecialchars($socialPlatform->link_url),
-            );
-        }
-    }
-
-    foreach ($podcast->fundingPlatforms as $fundingPlatform) {
-        $fundingPlatformElement = $channel->addChild(
-            'funding',
-            $fundingPlatform->link_content,
-            $podcast_namespace,
+            'http://www.w3.org/2005/Atom',
         );
-        $fundingPlatformElement->addAttribute(
-            'platform',
-            $fundingPlatform->slug,
-        );
-        if (!empty($socialPlatform->link_url)) {
-            $fundingPlatformElement->addAttribute(
-                'url',
-                htmlspecialchars($fundingPlatform->link_url),
+        $atom_link->addAttribute('href', $podcast->feed_url);
+        $atom_link->addAttribute('rel', 'self');
+        $atom_link->addAttribute('type', 'application/rss+xml');
+
+        if (!empty($podcast->new_feed_url)) {
+            $channel->addChild(
+                'new-feed-url',
+                $podcast->new_feed_url,
+                $itunes_namespace,
             );
         }
-    }
 
-    foreach ($podcast->persons as $podcastPerson) {
-        $podcastPersonElement = $channel->addChild(
-            'person',
-            htmlspecialchars($podcastPerson->person->full_name),
-            $podcast_namespace,
+        // the last build date corresponds to the creation of the feed.xml cache
+        $channel->addChild(
+            'lastBuildDate',
+            (new Time('now'))->format(DATE_RFC1123),
         );
-        if (
-            !empty($podcastPerson->person_role) &&
-            !empty($podcastPerson->person_group)
-        ) {
-            $podcastPersonElement->addAttribute(
-                'role',
-                htmlspecialchars(
-                    lang(
-                        "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label",
-                        [],
-                        'en',
-                    ),
-                ),
-            );
-        }
-        if (!empty($podcastPerson->person_group)) {
-            $podcastPersonElement->addAttribute(
-                'group',
-                htmlspecialchars(
-                    lang(
-                        "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label",
-                        [],
-                        'en',
-                    ),
-                ),
-            );
-        }
-        $podcastPersonElement->addAttribute(
-            'img',
-            $podcastPerson->person->image->large_url,
+        $channel->addChild(
+            'generator',
+            'Castopod Host - https://castopod.org/',
         );
-        if (!empty($podcastPerson->person->information_url)) {
-            $podcastPersonElement->addAttribute(
-                'href',
-                $podcastPerson->person->information_url,
-            );
-        }
-    }
+        $channel->addChild('docs', 'https://cyber.harvard.edu/rss/rss.html');
 
-    // set main category first, then other categories as apple
-    add_category_tag($channel, $podcast->category);
-    foreach ($podcast->other_categories as $other_category) {
-        add_category_tag($channel, $other_category);
-    }
+        $channel->addChild('title', $podcast->title);
+        $channel->addChildWithCDATA('description', $podcast->description_html);
 
-    $channel->addChild(
-        'explicit',
-        $podcast->parental_advisory === 'explicit' ? 'true' : 'false',
-        $itunes_namespace,
-    );
-
-    $channel->addChild(
-        'author',
-        $podcast->publisher ? $podcast->publisher : $podcast->owner_name,
-        $itunes_namespace,
-    );
-    $channel->addChild('link', $podcast->link);
-
-    $owner = $channel->addChild('owner', null, $itunes_namespace);
-    $owner->addChild('name', $podcast->owner_name, $itunes_namespace);
-    $owner->addChild('email', $podcast->owner_email, $itunes_namespace);
-
-    $channel->addChild('type', $podcast->type, $itunes_namespace);
-    $podcast->copyright && $channel->addChild('copyright', $podcast->copyright);
-    $podcast->is_blocked &&
-        $channel->addChild('block', 'Yes', $itunes_namespace);
-    $podcast->is_completed &&
-        $channel->addChild('complete', 'Yes', $itunes_namespace);
-
-    $image = $channel->addChild('image');
-    $image->addChild('url', $podcast->image->feed_url);
-    $image->addChild('title', $podcast->title);
-    $image->addChild('link', $podcast->link);
-
-    if (!empty($podcast->custom_rss)) {
-        array_to_rss(
-            [
-                'elements' => $podcast->custom_rss,
-            ],
-            $channel,
-        );
-    }
+        $itunes_image = $channel->addChild('image', null, $itunes_namespace);
 
-    foreach ($episodes as $episode) {
-        $item = $channel->addChild('item');
-        $item->addChild('title', $episode->title);
-        $enclosure = $item->addChild('enclosure');
-
-        $enclosure->addAttribute(
-            'url',
-            $episode->audio_file_analytics_url .
-                (empty($serviceSlug)
-                    ? ''
-                    : '?_from=' . urlencode($serviceSlug)),
-        );
-        $enclosure->addAttribute('length', $episode->audio_file_size);
-        $enclosure->addAttribute('type', $episode->audio_file_mimetype);
+        $itunes_image->addAttribute('href', $podcast->image->original_url);
 
-        $item->addChild('guid', $episode->guid);
-        $item->addChild(
-            'pubDate',
-            $episode->published_at->format(DATE_RFC1123),
-        );
-        if (!empty($episode->location_name)) {
-            $locationElement = $item->addChild(
+        $channel->addChild('language', $podcast->language_code);
+        if (!empty($podcast->location_name)) {
+            $locationElement = $channel->addChild(
                 'location',
-                htmlspecialchars($episode->location_name),
+                htmlspecialchars($podcast->location_name),
                 $podcast_namespace,
             );
-            if (!empty($episode->location_geo)) {
-                $locationElement->addAttribute('geo', $episode->location_geo);
+            if (!empty($podcast->location_geo)) {
+                $locationElement->addAttribute('geo', $podcast->location_geo);
             }
-            if (!empty($episode->location_osmid)) {
-                $locationElement->addAttribute('osm', $episode->location_osmid);
+            if (!empty($podcast->location_osmid)) {
+                $locationElement->addAttribute('osm', $podcast->location_osmid);
             }
         }
-        $item->addChildWithCDATA(
-            'description',
-            $episode->getDescriptionHtml($serviceSlug),
-        );
-        $item->addChild(
-            'duration',
-            $episode->audio_file_duration,
-            $itunes_namespace,
-        );
-        $item->addChild('link', $episode->link);
-        $episode_itunes_image = $item->addChild(
-            'image',
-            null,
-            $itunes_namespace,
-        );
-        $episode_itunes_image->addAttribute('href', $episode->image->feed_url);
-
-        $episode->parental_advisory &&
-            $item->addChild(
-                'explicit',
-                $episode->parental_advisory === 'explicit' ? 'true' : 'false',
-                $itunes_namespace,
-            );
-
-        $episode->number &&
-            $item->addChild('episode', $episode->number, $itunes_namespace);
-        $episode->season_number &&
-            $item->addChild(
-                'season',
-                $episode->season_number,
-                $itunes_namespace,
-            );
-        $item->addChild('episodeType', $episode->type, $itunes_namespace);
-
-        if ($episode->transcript_file_url) {
-            $transcriptElement = $item->addChild(
-                'transcript',
+        if (!empty($podcast->payment_pointer)) {
+            $valueElement = $channel->addChild(
+                'value',
                 null,
                 $podcast_namespace,
             );
-            $transcriptElement->addAttribute(
-                'url',
-                $episode->transcript_file_url,
+            $valueElement->addAttribute('type', 'webmonetization');
+            $valueElement->addAttribute('method', '');
+            $valueElement->addAttribute('suggested', '');
+            $recipientElement = $valueElement->addChild(
+                'valueRecipient',
+                null,
+                $podcast_namespace,
             );
-            $transcriptElement->addAttribute(
-                'type',
-                Mimes::guessTypeFromExtension(
-                    pathinfo($episode->transcript_file_url, PATHINFO_EXTENSION),
-                ),
+            $recipientElement->addAttribute('name', $podcast->owner_name);
+            $recipientElement->addAttribute('type', 'ILP');
+            $recipientElement->addAttribute(
+                'address',
+                $podcast->payment_pointer,
             );
-            $transcriptElement->addAttribute(
-                'language',
-                $podcast->language_code,
+            $recipientElement->addAttribute('split', 100);
+        }
+        $channel
+            ->addChild(
+                'locked',
+                $podcast->is_locked ? 'yes' : 'no',
+                $podcast_namespace,
+            )
+            ->addAttribute('owner', $podcast->owner_email);
+        if (!empty($podcast->imported_feed_url)) {
+            $channel->addChild(
+                'previousUrl',
+                $podcast->imported_feed_url,
+                $podcast_namespace,
             );
         }
 
-        if ($episode->chapters_file_url) {
-            $chaptersElement = $item->addChild(
-                'chapters',
+        foreach ($podcast->podcastingPlatforms as $podcastingPlatform) {
+            $podcastingPlatformElement = $channel->addChild(
+                'id',
                 null,
                 $podcast_namespace,
             );
-            $chaptersElement->addAttribute('url', $episode->chapters_file_url);
-            $chaptersElement->addAttribute('type', 'application/json+chapters');
+            $podcastingPlatformElement->addAttribute(
+                'platform',
+                $podcastingPlatform->slug,
+            );
+            if (!empty($podcastingPlatform->link_content)) {
+                $podcastingPlatformElement->addAttribute(
+                    'id',
+                    $podcastingPlatform->link_content,
+                );
+            }
+            if (!empty($podcastingPlatform->link_url)) {
+                $podcastingPlatformElement->addAttribute(
+                    'url',
+                    htmlspecialchars($podcastingPlatform->link_url),
+                );
+            }
+        }
+
+        foreach ($podcast->socialPlatforms as $socialPlatform) {
+            $socialPlatformElement = $channel->addChild(
+                'social',
+                $socialPlatform->link_content,
+                $podcast_namespace,
+            );
+            $socialPlatformElement->addAttribute(
+                'platform',
+                $socialPlatform->slug,
+            );
+            if (!empty($socialPlatform->link_url)) {
+                $socialPlatformElement->addAttribute(
+                    'url',
+                    htmlspecialchars($socialPlatform->link_url),
+                );
+            }
         }
 
-        foreach ($episode->soundbites as $soundbite) {
-            $soundbiteElement = $item->addChild(
-                'soundbite',
-                empty($soundbite->label) ? null : $soundbite->label,
+        foreach ($podcast->fundingPlatforms as $fundingPlatform) {
+            $fundingPlatformElement = $channel->addChild(
+                'funding',
+                $fundingPlatform->link_content,
                 $podcast_namespace,
             );
-            $soundbiteElement->addAttribute(
-                'start_time',
-                $soundbite->start_time,
+            $fundingPlatformElement->addAttribute(
+                'platform',
+                $fundingPlatform->slug,
             );
-            $soundbiteElement->addAttribute('duration', $soundbite->duration);
+            if (!empty($socialPlatform->link_url)) {
+                $fundingPlatformElement->addAttribute(
+                    'url',
+                    htmlspecialchars($fundingPlatform->link_url),
+                );
+            }
         }
 
-        foreach ($episode->persons as $episodePerson) {
-            $episodePersonElement = $item->addChild(
+        foreach ($podcast->persons as $podcastPerson) {
+            $podcastPersonElement = $channel->addChild(
                 'person',
-                htmlspecialchars($episodePerson->person->full_name),
+                htmlspecialchars($podcastPerson->person->full_name),
                 $podcast_namespace,
             );
             if (
-                !empty($episodePerson->person_role) &&
-                !empty($episodePerson->person_group)
+                !empty($podcastPerson->person_role) &&
+                !empty($podcastPerson->person_group)
             ) {
-                $episodePersonElement->addAttribute(
+                $podcastPersonElement->addAttribute(
                     'role',
                     htmlspecialchars(
                         lang(
-                            "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label",
+                            "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label",
                             [],
                             'en',
                         ),
                     ),
                 );
             }
-            if (!empty($episodePerson->person_group)) {
-                $episodePersonElement->addAttribute(
+            if (!empty($podcastPerson->person_group)) {
+                $podcastPersonElement->addAttribute(
                     'group',
                     htmlspecialchars(
                         lang(
-                            "PersonsTaxonomy.persons.{$episodePerson->person_group}.label",
+                            "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label",
                             [],
                             'en',
                         ),
                     ),
                 );
             }
-            $episodePersonElement->addAttribute(
+            $podcastPersonElement->addAttribute(
                 'img',
-                $episodePerson->person->image->large_url,
+                $podcastPerson->person->image->large_url,
             );
-            if (!empty($episodePerson->person->information_url)) {
-                $episodePersonElement->addAttribute(
+            if (!empty($podcastPerson->person->information_url)) {
+                $podcastPersonElement->addAttribute(
                     'href',
-                    $episodePerson->person->information_url,
+                    $podcastPerson->person->information_url,
                 );
             }
         }
 
-        $episode->is_blocked &&
-            $item->addChild('block', 'Yes', $itunes_namespace);
+        // set main category first, then other categories as apple
+        add_category_tag($channel, $podcast->category);
+        foreach ($podcast->other_categories as $other_category) {
+            add_category_tag($channel, $other_category);
+        }
+
+        $channel->addChild(
+            'explicit',
+            $podcast->parental_advisory === 'explicit' ? 'true' : 'false',
+            $itunes_namespace,
+        );
+
+        $channel->addChild(
+            'author',
+            $podcast->publisher ? $podcast->publisher : $podcast->owner_name,
+            $itunes_namespace,
+        );
+        $channel->addChild('link', $podcast->link);
+
+        $owner = $channel->addChild('owner', null, $itunes_namespace);
+
+        $owner->addChild('name', $podcast->owner_name, $itunes_namespace);
+
+        $owner->addChild('email', $podcast->owner_email, $itunes_namespace);
+
+        $channel->addChild('type', $podcast->type, $itunes_namespace);
+        $podcast->copyright &&
+            $channel->addChild('copyright', $podcast->copyright);
+        $podcast->is_blocked &&
+            $channel->addChild('block', 'Yes', $itunes_namespace);
+        $podcast->is_completed &&
+            $channel->addChild('complete', 'Yes', $itunes_namespace);
+
+        $image = $channel->addChild('image');
+        $image->addChild('url', $podcast->image->feed_url);
+        $image->addChild('title', $podcast->title);
+        $image->addChild('link', $podcast->link);
 
-        if (!empty($episode->custom_rss)) {
+        if (!empty($podcast->custom_rss)) {
             array_to_rss(
                 [
-                    'elements' => $episode->custom_rss,
+                    'elements' => $podcast->custom_rss,
                 ],
-                $item,
+                $channel,
             );
         }
-    }
 
-    return $rss->asXML();
+        foreach ($episodes as $episode) {
+            $item = $channel->addChild('item');
+            $item->addChild('title', $episode->title);
+            $enclosure = $item->addChild('enclosure');
+
+            $enclosure->addAttribute(
+                'url',
+                $episode->audio_file_analytics_url .
+                    (empty($serviceSlug)
+                        ? ''
+                        : '?_from=' . urlencode($serviceSlug)),
+            );
+            $enclosure->addAttribute('length', $episode->audio_file_size);
+            $enclosure->addAttribute('type', $episode->audio_file_mimetype);
+
+            $item->addChild('guid', $episode->guid);
+            $item->addChild(
+                'pubDate',
+                $episode->published_at->format(DATE_RFC1123),
+            );
+            if (!empty($episode->location_name)) {
+                $locationElement = $item->addChild(
+                    'location',
+                    htmlspecialchars($episode->location_name),
+                    $podcast_namespace,
+                );
+                if (!empty($episode->location_geo)) {
+                    $locationElement->addAttribute(
+                        'geo',
+                        $episode->location_geo,
+                    );
+                }
+                if (!empty($episode->location_osmid)) {
+                    $locationElement->addAttribute(
+                        'osm',
+                        $episode->location_osmid,
+                    );
+                }
+            }
+            $item->addChildWithCDATA(
+                'description',
+                $episode->getDescriptionHtml($serviceSlug),
+            );
+            $item->addChild(
+                'duration',
+                $episode->audio_file_duration,
+                $itunes_namespace,
+            );
+            $item->addChild('link', $episode->link);
+            $episode_itunes_image = $item->addChild(
+                'image',
+                null,
+                $itunes_namespace,
+            );
+            $episode_itunes_image->addAttribute(
+                'href',
+                $episode->image->feed_url,
+            );
+
+            $episode->parental_advisory &&
+                $item->addChild(
+                    'explicit',
+                    $episode->parental_advisory === 'explicit'
+                        ? 'true'
+                        : 'false',
+                    $itunes_namespace,
+                );
+
+            $episode->number &&
+                $item->addChild('episode', $episode->number, $itunes_namespace);
+            $episode->season_number &&
+                $item->addChild(
+                    'season',
+                    $episode->season_number,
+                    $itunes_namespace,
+                );
+            $item->addChild('episodeType', $episode->type, $itunes_namespace);
+
+            if ($episode->transcript_file_url) {
+                $transcriptElement = $item->addChild(
+                    'transcript',
+                    null,
+                    $podcast_namespace,
+                );
+                $transcriptElement->addAttribute(
+                    'url',
+                    $episode->transcript_file_url,
+                );
+                $transcriptElement->addAttribute(
+                    'type',
+                    Mimes::guessTypeFromExtension(
+                        pathinfo(
+                            $episode->transcript_file_url,
+                            PATHINFO_EXTENSION,
+                        ),
+                    ),
+                );
+                $transcriptElement->addAttribute(
+                    'language',
+                    $podcast->language_code,
+                );
+            }
+
+            if ($episode->chapters_file_url) {
+                $chaptersElement = $item->addChild(
+                    'chapters',
+                    null,
+                    $podcast_namespace,
+                );
+                $chaptersElement->addAttribute(
+                    'url',
+                    $episode->chapters_file_url,
+                );
+                $chaptersElement->addAttribute(
+                    'type',
+                    'application/json+chapters',
+                );
+            }
+
+            foreach ($episode->soundbites as $soundbite) {
+                $soundbiteElement = $item->addChild(
+                    'soundbite',
+                    empty($soundbite->label) ? null : $soundbite->label,
+                    $podcast_namespace,
+                );
+                $soundbiteElement->addAttribute(
+                    'start_time',
+                    $soundbite->start_time,
+                );
+                $soundbiteElement->addAttribute(
+                    'duration',
+                    $soundbite->duration,
+                );
+            }
+
+            foreach ($episode->persons as $episodePerson) {
+                $episodePersonElement = $item->addChild(
+                    'person',
+                    htmlspecialchars($episodePerson->person->full_name),
+                    $podcast_namespace,
+                );
+                if (
+                    !empty($episodePerson->person_role) &&
+                    !empty($episodePerson->person_group)
+                ) {
+                    $episodePersonElement->addAttribute(
+                        'role',
+                        htmlspecialchars(
+                            lang(
+                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label",
+                                [],
+                                'en',
+                            ),
+                        ),
+                    );
+                }
+                if (!empty($episodePerson->person_group)) {
+                    $episodePersonElement->addAttribute(
+                        'group',
+                        htmlspecialchars(
+                            lang(
+                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.label",
+                                [],
+                                'en',
+                            ),
+                        ),
+                    );
+                }
+                $episodePersonElement->addAttribute(
+                    'img',
+                    $episodePerson->person->image->large_url,
+                );
+                if (!empty($episodePerson->person->information_url)) {
+                    $episodePersonElement->addAttribute(
+                        'href',
+                        $episodePerson->person->information_url,
+                    );
+                }
+            }
+
+            $episode->is_blocked &&
+                $item->addChild('block', 'Yes', $itunes_namespace);
+
+            if (!empty($episode->custom_rss)) {
+                array_to_rss(
+                    [
+                        'elements' => $episode->custom_rss,
+                    ],
+                    $item,
+                );
+            }
+        }
+
+        return $rss->asXML();
+    }
 }
 
-/**
- * Adds <itunes:category> and <category> tags to node for a given category
- *
- * @param \SimpleXMLElement $node
- * @param \App\Entities\Category $category
- *
- * @return void
- */
-function add_category_tag($node, $category)
-{
-    $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
-
-    $itunes_category = $node->addChild('category', null, $itunes_namespace);
-    $itunes_category->addAttribute(
-        'text',
-        $category->parent
-            ? $category->parent->apple_category
-            : $category->apple_category,
-    );
-
-    if ($category->parent) {
-        $itunes_category_child = $itunes_category->addChild(
-            'category',
-            null,
-            $itunes_namespace,
+if (!function_exists('add_category_tag')) {
+    /**
+     * Adds <itunes:category> and <category> tags to node for a given category
+     */
+    function add_category_tag(SimpleXMLElement $node, Category $category): void
+    {
+        $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
+
+        $itunes_category = $node->addChild('category', null, $itunes_namespace);
+        $itunes_category->addAttribute(
+            'text',
+            $category->parent !== null
+                ? $category->parent->apple_category
+                : $category->apple_category,
         );
-        $itunes_category_child->addAttribute('text', $category->apple_category);
-        $node->addChild('category', $category->parent->apple_category);
+
+        if ($category->parent !== null) {
+            $itunes_category_child = $itunes_category->addChild(
+                'category',
+                null,
+                $itunes_namespace,
+            );
+            $itunes_category_child->addAttribute(
+                'text',
+                $category->apple_category,
+            );
+            $node->addChild('category', $category->parent->apple_category);
+        }
+        $node->addChild('category', $category->apple_category);
     }
-    $node->addChild('category', $category->apple_category);
 }
 
-/**
- * Converts XML to array
- *
- * @param \SimpleRSSElement $xmlNode
- *
- * @return array
- */
-function rss_to_array($xmlNode)
-{
-    $nameSpaces = [
-        '',
-        'http://www.itunes.com/dtds/podcast-1.0.dtd',
-        'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md',
-    ];
-    $arrayNode = [];
-    $arrayNode['name'] = $xmlNode->getName();
-    $arrayNode['namespace'] = $xmlNode->getNamespaces(false);
-    if (count($xmlNode->attributes()) > 0) {
+if (!function_exists('rss_to_array')) {
+    /**
+     * Converts XML to array
+     *
+     * FIXME: should be SimpleRSSElement
+     * @param SimpleXMLElement $xmlNode
+     */
+    function rss_to_array(SimpleXMLElement $xmlNode): array
+    {
+        $nameSpaces = [
+            '',
+            'http://www.itunes.com/dtds/podcast-1.0.dtd',
+            'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md',
+        ];
+        $arrayNode = [];
+        $arrayNode['name'] = $xmlNode->getName();
+        $arrayNode['namespace'] = $xmlNode->getNamespaces(false);
         foreach ($xmlNode->attributes() as $key => $value) {
             $arrayNode['attributes'][$key] = (string) $value;
         }
-    }
-    $textcontent = trim((string) $xmlNode);
-    if (strlen($textcontent) > 0) {
-        $arrayNode['content'] = $textcontent;
-    }
-    foreach ($nameSpaces as $currentNameSpace) {
-        foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) {
-            $arrayNode['elements'][] = rss_to_array($childXmlNode);
+        $textcontent = trim((string) $xmlNode);
+        if (strlen($textcontent) > 0) {
+            $arrayNode['content'] = $textcontent;
         }
+        foreach ($nameSpaces as $currentNameSpace) {
+            foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) {
+                $arrayNode['elements'][] = rss_to_array($childXmlNode);
+            }
+        }
+
+        return $arrayNode;
     }
-    return $arrayNode;
 }
 
-/**
- * Inserts array (converted to XML node) in XML node
- *
- * @param array $arrayNode
- * @param \SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached
- *
- */
-function array_to_rss($arrayNode, &$xmlNode)
-{
-    if (array_key_exists('elements', $arrayNode)) {
-        foreach ($arrayNode['elements'] as $childArrayNode) {
-            $childXmlNode = $xmlNode->addChild(
-                $childArrayNode['name'],
-                array_key_exists('content', $childArrayNode)
-                    ? $childArrayNode['content']
-                    : null,
-                empty($childArrayNode['namespace'])
-                    ? null
-                    : current($childArrayNode['namespace']),
-            );
-            if (array_key_exists('attributes', $childArrayNode)) {
-                foreach (
-                    $childArrayNode['attributes']
-                    as $attributeKey => $attributeValue
-                ) {
-                    $childXmlNode->addAttribute($attributeKey, $attributeValue);
+if (!function_exists('array_to_rss')) {
+    /**
+     * Inserts array (converted to XML node) in XML node
+     *
+     * @param SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached
+     */
+    function array_to_rss(array $arrayNode, SimpleRSSElement &$xmlNode)
+    {
+        if (array_key_exists('elements', $arrayNode)) {
+            foreach ($arrayNode['elements'] as $childArrayNode) {
+                $childXmlNode = $xmlNode->addChild(
+                    $childArrayNode['name'],
+                    $childArrayNode['content'] ?? null,
+                    empty($childArrayNode['namespace'])
+                        ? null
+                        : current($childArrayNode['namespace']),
+                );
+                if (array_key_exists('attributes', $childArrayNode)) {
+                    foreach (
+                        $childArrayNode['attributes']
+                        as $attributeKey => $attributeValue
+                    ) {
+                        $childXmlNode->addAttribute(
+                            $attributeKey,
+                            $attributeValue,
+                        );
+                    }
                 }
+                array_to_rss($childArrayNode, $childXmlNode);
             }
-            array_to_rss($childArrayNode, $childXmlNode);
         }
+
+        return $xmlNode;
     }
-    return $xmlNode;
 }
diff --git a/app/Helpers/svg_helper.php b/app/Helpers/svg_helper.php
index 56e36ccfaa..b612016921 100644
--- a/app/Helpers/svg_helper.php
+++ b/app/Helpers/svg_helper.php
@@ -6,43 +6,47 @@
  * @link       https://castopod.org/
  */
 
-/**
- * Returns the inline svg icon
- *
- * @param  string $name name of the icon file without the .svg extension
- * @param  string $class to be added to the svg string
- * @return string svg contents
- */
-function icon(string $name, string $class = '')
-{
-    $svg_contents = file_get_contents('assets/icons/' . $name . '.svg');
-    if ($class !== '') {
-        $svg_contents = str_replace(
-            '<svg',
-            '<svg class="' . $class . '"',
-            $svg_contents,
-        );
-    }
+if (!function_exists('icon')) {
+    /**
+     * Returns the inline svg icon
+     *
+     * @param  string $name name of the icon file without the .svg extension
+     * @param  string $class to be added to the svg string
+     * @return string svg contents
+     */
+    function icon(string $name, string $class = ''): string
+    {
+        $svg_contents = file_get_contents('assets/icons/' . $name . '.svg');
+        if ($class !== '') {
+            $svg_contents = str_replace(
+                '<svg',
+                '<svg class="' . $class . '"',
+                $svg_contents,
+            );
+        }
 
-    return $svg_contents;
+        return $svg_contents;
+    }
 }
 
-/**
- * Returns the inline svg image
- *
- * @param  string $name name of the image file without the .svg extension
- * @param  string $class to be added to the svg string
- * @return string svg contents
- */
-function svg($name, $class = null)
-{
-    $svg_contents = file_get_contents('assets/images/' . $name . '.svg');
-    if ($class) {
-        $svg_contents = str_replace(
-            '<svg',
-            '<svg class="' . $class . '"',
-            $svg_contents,
-        );
+if (!function_exists('svg')) {
+    /**
+     * Returns the inline svg image
+     *
+     * @param  string $name name of the image file without the .svg extension
+     * @param  string $class to be added to the svg string
+     * @return string svg contents
+     */
+    function svg(string $name, ?string $class = null): string
+    {
+        $svg_contents = file_get_contents('assets/images/' . $name . '.svg');
+        if ($class) {
+            $svg_contents = str_replace(
+                '<svg',
+                '<svg class="' . $class . '"',
+                $svg_contents,
+            );
+        }
+        return $svg_contents;
     }
-    return $svg_contents;
 }
diff --git a/app/Helpers/url_helper.php b/app/Helpers/url_helper.php
index 9e80311dca..0e860743cd 100644
--- a/app/Helpers/url_helper.php
+++ b/app/Helpers/url_helper.php
@@ -1,5 +1,11 @@
 <?php
 
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
 if (!function_exists('host_url')) {
     /**
      * Return the host URL to use in views
@@ -24,10 +30,8 @@ if (!function_exists('host_url')) {
 if (!function_exists('current_season_url')) {
     /**
      * Return the podcast URL with season number to use in views
-     *
-     * @return string
      */
-    function current_season_url()
+    function current_season_url(): string
     {
         $season_query_string = '';
         if (isset($_GET['season'])) {
@@ -42,36 +46,27 @@ if (!function_exists('current_season_url')) {
 if (!function_exists('location_url')) {
     /**
      * Returns URL to display from location info
-     *
-     * @param string $locationName
-     * @param string $locationGeo
-     * @param string $locationOsmid
-     *
-     * @return string
      */
-    function location_url($locationName, $locationGeo, $locationOsmid)
-    {
-        $uri = '';
-
+    function location_url(
+        string $locationName,
+        ?string $locationGeo = null,
+        ?string $locationOsmid = null
+    ): string {
         if (!empty($locationOsmid)) {
-            $uri =
-                'https://www.openstreetmap.org/' .
+            return 'https://www.openstreetmap.org/' .
                 ['N' => 'node', 'W' => 'way', 'R' => 'relation'][
                     substr($locationOsmid, 0, 1)
                 ] .
                 '/' .
                 substr($locationOsmid, 1);
-        } elseif (!empty($locationGeo)) {
-            $uri =
-                'https://www.openstreetmap.org/#map=17/' .
+        }
+        if (!empty($locationGeo)) {
+            return 'https://www.openstreetmap.org/#map=17/' .
                 str_replace(',', '/', substr($locationGeo, 4));
-        } elseif (!empty($locationName)) {
-            $uri =
-                'https://www.openstreetmap.org/search?query=' .
-                urlencode($locationName);
         }
 
-        return $uri;
+        return 'https://www.openstreetmap.org/search?query=' .
+            urlencode($locationName);
     }
 }
 //--------------------------------------------------------------------
@@ -81,27 +76,29 @@ if (!function_exists('extract_params_from_episode_uri')) {
      * Returns podcast name and episode slug from episode string uri
      *
      * @param URI $episodeUri
-     * @return string|null
      */
-    function extract_params_from_episode_uri($episodeUri)
+    function extract_params_from_episode_uri($episodeUri): ?array
     {
         preg_match(
-            '/@(?P<podcastName>[a-zA-Z0-9\_]{1,32})\/episodes\/(?P<episodeSlug>[a-zA-Z0-9\-]{1,191})/',
+            '~@(?P<podcastName>[a-zA-Z0-9\_]{1,32})\/episodes\/(?P<episodeSlug>[a-zA-Z0-9\-]{1,191})~',
             $episodeUri->getPath(),
             $matches,
         );
 
+        if ($matches === []) {
+            return null;
+        }
+
         if (
-            $matches &&
-            array_key_exists('podcastName', $matches) &&
-            array_key_exists('episodeSlug', $matches)
+            !array_key_exists('podcastName', $matches) ||
+            !array_key_exists('episodeSlug', $matches)
         ) {
-            return [
-                'podcastName' => $matches['podcastName'],
-                'episodeSlug' => $matches['episodeSlug'],
-            ];
+            return null;
         }
 
-        return null;
+        return [
+            'podcastName' => $matches['podcastName'],
+            'episodeSlug' => $matches['episodeSlug'],
+        ];
     }
 }
diff --git a/app/Language/en/ActivityPub.php b/app/Language/en/ActivityPub.php
index 38b9573dd0..62a78d8164 100644
--- a/app/Language/en/ActivityPub.php
+++ b/app/Language/en/ActivityPub.php
@@ -17,17 +17,17 @@ return [
         'submit' => 'Proceed to follow',
     ],
     'favourite' => [
-        'title' => 'Favourite {actorDisplayName}\'s note',
+        'title' => "Favourite {actorDisplayName}'s note",
         'subtitle' => 'You are going to favourite:',
         'submit' => 'Proceed to favourite',
     ],
     'reblog' => [
-        'title' => 'Share {actorDisplayName}\'s note',
+        'title' => "Share {actorDisplayName}'s note",
         'subtitle' => 'You are going to share:',
         'submit' => 'Proceed to share',
     ],
     'reply' => [
-        'title' => 'Reply to {actorDisplayName}\'s note',
+        'title' => "Reply to {actorDisplayName}'s note",
         'subtitle' => 'You are going to reply to:',
         'submit' => 'Proceed to reply',
     ],
diff --git a/app/Language/en/Contributor.php b/app/Language/en/Contributor.php
index 540dc57113..4021ad9d1e 100644
--- a/app/Language/en/Contributor.php
+++ b/app/Language/en/Contributor.php
@@ -8,7 +8,7 @@
 
 return [
     'podcast_contributors' => 'Podcast contributors',
-    'view' => '{username}\'s contribution to {podcastName}',
+    'view' => "{username}'s contribution to {podcastName}",
     'add' => 'Add contributor',
     'add_contributor' => 'Add a contributor for {0}',
     'edit_role' => 'Update role for {0}',
@@ -28,10 +28,10 @@ return [
         'podcast_admin' => 'Podcast admin',
     ],
     'messages' => [
-        'removeOwnerContributorError' => 'You can\'t remove the podcast owner!',
+        'removeOwnerContributorError' => "You can't remove the podcast owner!",
         'removeContributorSuccess' =>
             'You have successfully removed {username} from {podcastTitle}',
         'alreadyAddedError' =>
-            'The contributor you\'re trying to add has already been added!',
+            "The contributor you're trying to add has already been added!",
     ],
 ];
diff --git a/app/Language/en/Countries.php b/app/Language/en/Countries.php
index ba39901922..8e915b5dbb 100644
--- a/app/Language/en/Countries.php
+++ b/app/Language/en/Countries.php
@@ -51,7 +51,7 @@ return [
     'CF' => 'Central African Republic',
     'CG' => 'Congo',
     'CH' => 'Switzerland',
-    'CI' => 'Côte d\'Ivoire',
+    'CI' => "Côte d'Ivoire",
     'CK' => 'Cook Islands',
     'CL' => 'Chile',
     'CM' => 'Cameroon',
@@ -128,12 +128,12 @@ return [
     'KI' => 'Kiribati',
     'KM' => 'Comoros',
     'KN' => 'Saint Kitts and Nevis',
-    'KP' => 'Korea, Democratic People\'s Republic of',
+    'KP' => "Korea, Democratic People's Republic of",
     'KR' => 'Korea, Republic of',
     'KW' => 'Kuwait',
     'KY' => 'Cayman Islands',
     'KZ' => 'Kazakhstan',
-    'LA' => 'Lao People\'s Democratic Republic',
+    'LA' => "Lao People's Democratic Republic",
     'LB' => 'Lebanon',
     'LC' => 'Saint Lucia',
     'LI' => 'Liechtenstein',
diff --git a/app/Language/en/Episode.php b/app/Language/en/Episode.php
index 5eaa06caae..44a817fba7 100644
--- a/app/Language/en/Episode.php
+++ b/app/Language/en/Episode.php
@@ -127,7 +127,7 @@ return [
     ],
     'unpublish_form' => [
         'disclaimer' =>
-            'Unpublishing the episode will delete all the notes associated with the episode and remove it from the podcast\'s RSS feed.',
+            "Unpublishing the episode will delete all the notes associated with the episode and remove it from the podcast's RSS feed.",
         'understand' => 'I understand, I want to unpublish the episode',
         'submit' => 'Unpublish',
     ],
diff --git a/app/Language/en/Install.php b/app/Language/en/Install.php
index b4428f8d66..df78956d49 100644
--- a/app/Language/en/Install.php
+++ b/app/Language/en/Install.php
@@ -31,7 +31,7 @@ return [
         'db_password' => 'Database password',
         'db_prefix' => 'Database prefix',
         'db_prefix_hint' =>
-            'The prefix of the Castopod table names, leave as is if you don\'t know what it means.',
+            "The prefix of the Castopod table names, leave as is if you don't know what it means.",
         'cache_config' => 'Cache configuration',
         'cache_config_hint' =>
             'Choose your preferred cache handler. Leave it as the default value if you have no clue what it means.',
@@ -54,6 +54,6 @@ return [
         'databaseConnectError' =>
             'Castopod could not connect to your database. Edit your database configuration and try again.',
         'writeError' =>
-            'Couldn\'t create/write the `.env` file. You must create it manually by following the `.env.example` file template in the Castopod package.',
+            "Couldn't create/write the `.env` file. You must create it manually by following the `.env.example` file template in the Castopod package.",
     ],
 ];
diff --git a/app/Language/en/MyAccount.php b/app/Language/en/MyAccount.php
index b9b001d8d0..ccdced98be 100644
--- a/app/Language/en/MyAccount.php
+++ b/app/Language/en/MyAccount.php
@@ -10,8 +10,7 @@ return [
     'info' => 'My account info',
     'changePassword' => 'Change my password',
     'messages' => [
-        'wrongPasswordError' =>
-            'You\'ve entered the wrong password, try again.',
+        'wrongPasswordError' => "You've entered the wrong password, try again.",
         'passwordChangeSuccess' => 'Password has been successfully changed!',
     ],
 ];
diff --git a/app/Language/en/Note.php b/app/Language/en/Note.php
index ba75903229..7c4daaa8f0 100644
--- a/app/Language/en/Note.php
+++ b/app/Language/en/Note.php
@@ -7,7 +7,7 @@
  */
 
 return [
-    'title' => '{actorDisplayName}\'s Note',
+    'title' => "{actorDisplayName}'s Note",
     'back_to_actor_notes' => 'Back to {actor} notes',
     'actor_shared' => '{actor} shared',
     'reply_to' => 'Reply to @{actorUsername}',
diff --git a/app/Language/en/User.php b/app/Language/en/User.php
index 3c84d274bb..493b3fe65c 100644
--- a/app/Language/en/User.php
+++ b/app/Language/en/User.php
@@ -7,13 +7,13 @@
  */
 
 return [
-    'edit_roles' => 'Edit {username}\'s roles',
+    'edit_roles' => "Edit {username}'s roles",
     'forcePassReset' => 'Force pass reset',
     'ban' => 'Ban',
     'unban' => 'Unban',
     'delete' => 'Delete',
     'create' => 'New user',
-    'view' => '{username}\'s info',
+    'view' => "{username}'s info",
     'all_users' => 'All users',
     'list' => [
         'user' => 'User',
@@ -38,7 +38,7 @@ return [
         'createSuccess' =>
             'User created successfully! {username} will be prompted with a password reset upon first authentication.',
         'rolesEditSuccess' =>
-            '{username}\'s roles have been successfully updated.',
+            "{username}'s roles have been successfully updated.",
         'forcePassResetSuccess' =>
             '{username} will be prompted with a password reset upon next visit.',
         'banSuccess' => '{username} has been banned.',
diff --git a/app/Language/fr/Countries.php b/app/Language/fr/Countries.php
index 48b3654129..f37fbb0ab3 100644
--- a/app/Language/fr/Countries.php
+++ b/app/Language/fr/Countries.php
@@ -116,7 +116,7 @@ return [
     'VI ' => 'Îles Vierges Des États-Unis',
     'IN ' => 'Inde',
     'ID ' => 'Indonésie',
-    'IR ' => 'Iran, République Islamique D\'',
+    'IR ' => "Iran, République Islamique D'",
     'IQ ' => 'Iraq',
     'IE ' => 'Irlande',
     'IS ' => 'Islande',
@@ -176,7 +176,7 @@ return [
     'NO ' => 'Norvège',
     'NC ' => 'Nouvelle-Calédonie',
     'NZ ' => 'Nouvelle-Zélande',
-    'IO ' => 'Océan Indien, Territoire Britannique De L\'',
+    'IO ' => "Océan Indien, Territoire Britannique De L'",
     'OM ' => 'Oman',
     'UG ' => 'Ouganda',
     'UZ ' => 'Ouzbékistan',
diff --git a/app/Language/fr/Episode.php b/app/Language/fr/Episode.php
index df4a5d90b2..39e860e578 100644
--- a/app/Language/fr/Episode.php
+++ b/app/Language/fr/Episode.php
@@ -112,17 +112,14 @@ return [
         'submit_create' => 'Créer l’épisode',
         'submit_edit' => 'Enregistrer l’épisode',
     ],
-    'publish_form' => [
-        'publication_date' => 'Date de publication',
-        'publication_date_clear' => 'Effacer la date de publication',
-        'publication_date_hint' =>
-            'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit être au format YYYY-MM-DD HH:mm',
-    ],
     'publish_form' => [
         'note' => 'Votre note',
         'note_hint' =>
             'Le message que vous écrirez sera diffusé à toutes les personnes qui vous suivent dans le fédiverse.',
         'publication_date' => 'Date de publication',
+        'publication_date_clear' => 'Effacer la date de publication',
+        'publication_date_hint' =>
+            'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit être au format YYYY-MM-DD HH:mm',
         'publication_method' => [
             'now' => 'Maintenant',
             'schedule' => 'Planifier',
diff --git a/app/Language/fr/PodcastNavigation.php b/app/Language/fr/PodcastNavigation.php
index 77bf5041ad..cc8bea6410 100644
--- a/app/Language/fr/PodcastNavigation.php
+++ b/app/Language/fr/PodcastNavigation.php
@@ -26,7 +26,6 @@ return [
     'platforms-podcasting' => 'Podcasts',
     'platforms-social' => 'Réseaux Sociaux',
     'platforms-funding' => 'Financement',
-    'platforms' => 'Plateformes du podcast',
     'podcast-analytics' => 'Vue d’ensemble',
     'podcast-analytics-webpages' => 'Visites des pages web',
     'podcast-analytics-locations' => 'Localisations',
diff --git a/app/Libraries/ActivityPub/ActivityRequest.php b/app/Libraries/ActivityPub/ActivityRequest.php
index 3386dc77c9..9f8925a738 100644
--- a/app/Libraries/ActivityPub/ActivityRequest.php
+++ b/app/Libraries/ActivityPub/ActivityRequest.php
@@ -8,23 +8,28 @@
 
 namespace ActivityPub;
 
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\HTTP\CURLRequest;
+use CodeIgniter\HTTP\URI;
+use ActivityPub\Core\Activity;
+use Config\Services;
 use CodeIgniter\I18n\Time;
 use phpseclib\Crypt\RSA;
 
 class ActivityRequest
 {
     /**
-     * @var \CodeIgniter\HTTP\CURLRequest
+     * @var CURLRequest
      */
     protected $request;
 
     /**
-     * @var \CodeIgniter\HTTP\URI
+     * @var URI
      */
     protected $uri;
 
     /**
-     * @var \ActivityPub\Core\Activity|null
+     * @var Activity|null
      */
     protected $activity;
 
@@ -44,33 +49,33 @@ class ActivityRequest
      */
     public function __construct($uri, $activityPayload = null)
     {
-        $this->request = \Config\Services::curlrequest();
+        $this->request = Services::curlrequest();
 
         if ($activityPayload) {
             $this->request->setBody($activityPayload);
         }
 
-        $this->uri = new \CodeIgniter\HTTP\URI($uri);
+        $this->uri = new URI($uri);
     }
 
-    public function post()
+    public function post(): void
     {
         // send Message to Fediverse instance
         $this->request->post($this->uri, $this->options);
     }
 
-    public function get()
+    public function get(): ResponseInterface
     {
         return $this->request->get($this->uri, $this->options);
     }
 
-    public function getDomain()
+    public function getDomain(): string
     {
         return $this->uri->getHost() .
             ($this->uri->getPort() ? ':' . $this->uri->getPort() : '');
     }
 
-    public function sign($keyId, $privateKey)
+    public function sign($keyId, $privateKey): void
     {
         $rsa = new RSA();
         $rsa->loadKey($privateKey); // private key
@@ -79,7 +84,7 @@ class ActivityRequest
 
         $path =
             $this->uri->getPath() .
-            ($this->uri->getQuery() ? "?{$this->uri->getQuery()}" : '');
+            ($this->uri->getQuery() !== '' ? "?{$this->uri->getQuery()}" : '');
         $host = $this->uri->getHost();
         $date = Time::now('GMT')->format('D, d M Y H:i:s T');
         $digest = 'SHA-256=' . base64_encode($this->getBodyDigest());
@@ -87,7 +92,7 @@ class ActivityRequest
         $contentLength = strval(strlen($this->request->getBody()));
         $userAgent = 'Castopod';
 
-        $plainText = "(request-target): post $path\nhost: $host\ndate: $date\ndigest: $digest\ncontent-type: $contentType\ncontent-length: $contentLength\nuser-agent: $userAgent";
+        $plainText = "(request-target): post {$path}\nhost: {$host}\ndate: {$date}\ndigest: {$digest}\ncontent-type: {$contentType}\ncontent-length: {$contentLength}\nuser-agent: {$userAgent}";
 
         $signature = $rsa->sign($plainText);
 
@@ -102,7 +107,7 @@ class ActivityRequest
             'headers' => [
                 'Content-Type' => $contentType,
                 'Content-Length' => $contentLength,
-                'Authorization' => "Signature $signatureHeader",
+                'Authorization' => "Signature {$signatureHeader}",
                 'Signature' => $signatureHeader,
                 'Host' => $host,
                 'Date' => $date,
@@ -112,7 +117,7 @@ class ActivityRequest
         ];
     }
 
-    protected function getBodyDigest()
+    protected function getBodyDigest(): string
     {
         return hash('sha256', $this->request->getBody(), true);
     }
diff --git a/app/Libraries/ActivityPub/Config/ActivityPub.php b/app/Libraries/ActivityPub/Config/ActivityPub.php
index e96460d47d..3ac4e4fc67 100644
--- a/app/Libraries/ActivityPub/Config/ActivityPub.php
+++ b/app/Libraries/ActivityPub/Config/ActivityPub.php
@@ -16,25 +16,43 @@ class ActivityPub extends BaseConfig
      * --------------------------------------------------------------------
      * ActivityPub Objects
      * --------------------------------------------------------------------
+     * @var string
      */
     public $actorObject = 'ActivityPub\Objects\ActorObject';
+
+    /**
+     * @var string
+     */
     public $noteObject = 'ActivityPub\Objects\NoteObject';
 
     /**
      * --------------------------------------------------------------------
      * Default avatar and cover images
      * --------------------------------------------------------------------
+     * @var string
      */
     public $defaultAvatarImagePath = 'assets/images/avatar-default.jpg';
+
+    /**
+     * @var string
+     */
     public $defaultAvatarImageMimetype = 'image/jpeg';
 
+    /**
+     * @var string
+     */
     public $defaultCoverImagePath = 'assets/images/cover-default.jpg';
+
+    /**
+     * @var string
+     */
     public $defaultCoverImageMimetype = 'image/jpeg';
 
     /**
      * --------------------------------------------------------------------
      * Cache options
      * --------------------------------------------------------------------
+     * @var string
      */
     public $cachePrefix = 'ap_';
 }
diff --git a/app/Libraries/ActivityPub/Config/Routes.php b/app/Libraries/ActivityPub/Config/Routes.php
index 9f68c1a986..011991357f 100644
--- a/app/Libraries/ActivityPub/Config/Routes.php
+++ b/app/Libraries/ActivityPub/Config/Routes.php
@@ -19,14 +19,14 @@ $routes->addPlaceholder('noteAction', '\bfavourite|\breblog|\breply');
 
 $routes->group('', ['namespace' => 'ActivityPub\Controllers'], function (
     $routes
-) {
+): void {
     // webfinger
     $routes->get('.well-known/webfinger', 'WebFingerController', [
         'as' => 'webfinger',
     ]);
 
     // Actor
-    $routes->group('@(:actorUsername)', function ($routes) {
+    $routes->group('@(:actorUsername)', function ($routes): void {
         // Actor
         $routes->get('/', 'ActorController/$1', [
             'as' => 'actor',
diff --git a/app/Libraries/ActivityPub/Controllers/ActorController.php b/app/Libraries/ActivityPub/Controllers/ActorController.php
index e6cdc3a284..bdaa53df6d 100644
--- a/app/Libraries/ActivityPub/Controllers/ActorController.php
+++ b/app/Libraries/ActivityPub/Controllers/ActorController.php
@@ -8,6 +8,13 @@
 
 namespace ActivityPub\Controllers;
 
+use ActivityPub\Entities\Actor;
+use ActivityPub\Config\ActivityPub;
+use CodeIgniter\HTTP\RedirectResponse;
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use ActivityPub\Entities\Note;
+use CodeIgniter\HTTP\Exceptions\HTTPException;
 use ActivityPub\Objects\OrderedCollectionObject;
 use ActivityPub\Objects\OrderedCollectionPage;
 use CodeIgniter\Controller;
@@ -15,15 +22,18 @@ use CodeIgniter\I18n\Time;
 
 class ActorController extends Controller
 {
+    /**
+     * @var string[]
+     */
     protected $helpers = ['activitypub'];
 
     /**
-     * @var \ActivityPub\Entities\Actor
+     * @var Actor
      */
     protected $actor;
 
     /**
-     * @var \ActivityPub\Config\ActivityPub
+     * @var ActivityPub
      */
     protected $config;
 
@@ -34,21 +44,20 @@ class ActorController extends Controller
 
     public function _remap($method, ...$params)
     {
-        if (count($params) > 0) {
-            if (
-                !($this->actor = model('ActorModel')->getActorByUsername(
-                    $params[0],
-                ))
-            ) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
-            }
+        if (
+            count($params) > 0 &&
+            !($this->actor = model('ActorModel')->getActorByUsername(
+                $params[0],
+            ))
+        ) {
+            throw PageNotFoundException::forPageNotFound();
         }
         unset($params[0]);
 
         return $this->$method(...$params);
     }
 
-    public function index()
+    public function index(): RedirectResponse
     {
         $actorObjectClass = $this->config->actorObject;
         $actorObject = new $actorObjectClass($this->actor);
@@ -61,7 +70,7 @@ class ActorController extends Controller
     /**
      * Handles incoming requests from fediverse servers
      */
-    public function inbox()
+    public function inbox(): ResponseInterface
     {
         // get json body and parse it
         $payload = $this->request->getJSON();
@@ -75,56 +84,41 @@ class ActorController extends Controller
             $payloadActor->id,
             $this->actor->id,
             null,
-            json_encode($payload),
+            json_encode($payload, JSON_THROW_ON_ERROR),
         );
 
         // switch/case on activity type
         switch ($payload->type) {
             case 'Create':
-                switch ($payload->object->type) {
-                    case 'Note':
-                        if (!$payload->object->inReplyTo) {
-                            return $this->response
-                                ->setStatusCode(501)
-                                ->setJSON([]);
-                        }
-
-                        $replyToNote = model('NoteModel')->getNoteByUri(
-                            $payload->object->inReplyTo,
-                        );
-
-                        // TODO: strip content from html to retrieve message
-                        // remove all html tags and reconstruct message with mentions?
-                        extract_text_from_html($payload->object->content);
-
-                        $reply = new \ActivityPub\Entities\Note([
-                            'uri' => $payload->object->id,
-                            'actor_id' => $payloadActor->id,
-                            'in_reply_to_id' => $replyToNote->id,
-                            'message' => $payload->object->content,
-                            'published_at' => Time::parse(
-                                $payload->object->published,
-                            ),
-                        ]);
-
-                        $noteId = model('NoteModel')->addReply(
-                            $reply,
-                            true,
-                            false,
-                        );
-
-                        model('ActivityModel')->update($activityId, [
-                            'note_id' => service('uuid')
-                                ->fromBytes($noteId)
-                                ->getString(),
-                        ]);
-
-                        return $this->response->setStatusCode(200)->setJSON([]);
-                    default:
-                        // return not handled undo error (501 = not implemented)
+                if ($payload->object->type == 'Note') {
+                    if (!$payload->object->inReplyTo) {
                         return $this->response->setStatusCode(501)->setJSON([]);
+                    }
+                    $replyToNote = model('NoteModel')->getNoteByUri(
+                        $payload->object->inReplyTo,
+                    );
+                    // TODO: strip content from html to retrieve message
+                    // remove all html tags and reconstruct message with mentions?
+                    extract_text_from_html($payload->object->content);
+                    $reply = new Note([
+                        'uri' => $payload->object->id,
+                        'actor_id' => $payloadActor->id,
+                        'in_reply_to_id' => $replyToNote->id,
+                        'message' => $payload->object->content,
+                        'published_at' => Time::parse(
+                            $payload->object->published,
+                        ),
+                    ]);
+                    $noteId = model('NoteModel')->addReply($reply, true, false);
+                    model('ActivityModel')->update($activityId, [
+                        'note_id' => service('uuid')
+                            ->fromBytes($noteId)
+                            ->getString(),
+                    ]);
+                    return $this->response->setStatusCode(200)->setJSON([]);
                 }
-                break;
+                // return not handled undo error (501 = not implemented)
+                return $this->response->setStatusCode(501)->setJSON([]);
             case 'Delete':
                 $noteToDelete = model('NoteModel')->getNoteByUri(
                     $payload->object->id,
@@ -234,7 +228,7 @@ class ActorController extends Controller
         }
     }
 
-    public function outbox()
+    public function outbox(): RedirectResponse
     {
         // get published activities by publication date
         $actorActivity = model('ActivityModel')
@@ -257,7 +251,7 @@ class ActorController extends Controller
             $pager = $actorActivity->pager;
             $orderedItems = [];
             foreach ($paginatedActivity as $activity) {
-                array_push($orderedItems, $activity->payload);
+                $orderedItems[] = $activity->payload;
             }
             $collection = new OrderedCollectionPage($pager, $orderedItems);
         }
@@ -267,7 +261,7 @@ class ActorController extends Controller
             ->setBody($collection->toJSON());
     }
 
-    public function followers()
+    public function followers(): RedirectResponse
     {
         // get followers for a specific actor
         $followers = model('ActorModel')
@@ -295,7 +289,7 @@ class ActorController extends Controller
 
             $orderedItems = [];
             foreach ($paginatedFollowers as $follower) {
-                array_push($orderedItems, $follower->uri);
+                $orderedItems[] = $follower->uri;
             }
             $followersCollection = new OrderedCollectionPage(
                 $pager,
@@ -308,6 +302,9 @@ class ActorController extends Controller
             ->setBody($followersCollection->toJSON());
     }
 
+    /**
+     * @return mixed|ResponseInterface
+     */
     public function attemptFollow()
     {
         $rules = [
@@ -334,7 +331,7 @@ class ActorController extends Controller
 
                 $data = get_webfinger_data($username, $domain);
             }
-        } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) {
+        } catch (HTTPException $httpException) {
             return redirect()
                 ->back()
                 ->withInput()
@@ -361,16 +358,16 @@ class ActorController extends Controller
         );
     }
 
-    public function activity($activityId)
+    public function activity($activityId): RedirectResponse
     {
         if (
             !($activity = model('ActivityModel')->getActivityById($activityId))
         ) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            throw PageNotFoundException::forPageNotFound();
         }
 
         return $this->response
             ->setContentType('application/activity+json')
-            ->setBody(json_encode($activity->payload));
+            ->setBody(json_encode($activity->payload, JSON_THROW_ON_ERROR));
     }
 }
diff --git a/app/Libraries/ActivityPub/Controllers/BlockController.php b/app/Libraries/ActivityPub/Controllers/BlockController.php
index 87ffe0bbc8..f38d84e150 100644
--- a/app/Libraries/ActivityPub/Controllers/BlockController.php
+++ b/app/Libraries/ActivityPub/Controllers/BlockController.php
@@ -12,6 +12,9 @@ use CodeIgniter\Controller;
 
 class BlockController extends Controller
 {
+    /**
+     * @var string[]
+     */
     protected $helpers = ['activitypub'];
 
     public function attemptBlockActor()
@@ -32,7 +35,7 @@ class BlockController extends Controller
         if ($parts = split_handle($handle)) {
             extract($parts);
 
-            if (!($actor = get_or_create_actor($username, $domain))) {
+            if (($actor = get_or_create_actor($username, $domain)) === null) {
                 return redirect()
                     ->back()
                     ->withInput()
diff --git a/app/Libraries/ActivityPub/Controllers/NoteController.php b/app/Libraries/ActivityPub/Controllers/NoteController.php
index 048beaa059..708e92d746 100644
--- a/app/Libraries/ActivityPub/Controllers/NoteController.php
+++ b/app/Libraries/ActivityPub/Controllers/NoteController.php
@@ -8,6 +8,11 @@
 
 namespace ActivityPub\Controllers;
 
+use CodeIgniter\HTTP\RedirectResponse;
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use ActivityPub\Entities\Note;
+use CodeIgniter\HTTP\Exceptions\HTTPException;
 use ActivityPub\Config\ActivityPub;
 use ActivityPub\Objects\OrderedCollectionObject;
 use ActivityPub\Objects\OrderedCollectionPage;
@@ -16,15 +21,18 @@ use CodeIgniter\I18n\Time;
 
 class NoteController extends Controller
 {
+    /**
+     * @var string[]
+     */
     protected $helpers = ['activitypub'];
 
     /**
-     * @var \ActivityPub\Entities\Note|null
+     * @var Note|null
      */
     protected $note;
 
     /**
-     * @var \ActivityPub\Config\ActivityPub
+     * @var ActivityPub
      */
     protected $config;
 
@@ -36,14 +44,14 @@ class NoteController extends Controller
     public function _remap($method, ...$params)
     {
         if (!($this->note = model('NoteModel')->getNoteById($params[0]))) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            throw PageNotFoundException::forPageNotFound();
         }
         unset($params[0]);
 
         return $this->$method(...$params);
     }
 
-    public function index()
+    public function index(): RedirectResponse
     {
         $noteObjectClass = $this->config->noteObject;
         $noteObject = new $noteObjectClass($this->note);
@@ -53,7 +61,7 @@ class NoteController extends Controller
             ->setBody($noteObject->toJSON());
     }
 
-    public function replies()
+    public function replies(): RedirectResponse
     {
         // get note replies
         $noteReplies = model('NoteModel')
@@ -84,7 +92,7 @@ class NoteController extends Controller
             $noteObjectClass = $this->config->noteObject;
             foreach ($paginatedReplies as $reply) {
                 $replyObject = new $noteObjectClass($reply);
-                array_push($orderedItems, $replyObject->toJSON());
+                $orderedItems[] = $replyObject->toJSON();
             }
             $collection = new OrderedCollectionPage($pager, $orderedItems);
         }
@@ -108,7 +116,7 @@ class NoteController extends Controller
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $newNote = new \ActivityPub\Entities\Note([
+        $newNote = new Note([
             'actor_id' => $this->request->getPost('actor_id'),
             'message' => $this->request->getPost('message'),
             'published_at' => Time::now(),
@@ -119,7 +127,7 @@ class NoteController extends Controller
                 ->back()
                 ->withInput()
                 // TODO: translate
-                ->with('error', 'Couldn\'t create Note');
+                ->with('error', "Couldn't create Note");
         }
 
         // Note without preview card has been successfully created
@@ -184,7 +192,7 @@ class NoteController extends Controller
                 ->with('errors', $this->validator->getErrors());
         }
 
-        $newReplyNote = new \ActivityPub\Entities\Note([
+        $newReplyNote = new Note([
             'actor_id' => $this->request->getPost('actor_id'),
             'in_reply_to_id' => $this->note->id,
             'message' => $this->request->getPost('message'),
@@ -196,14 +204,17 @@ class NoteController extends Controller
                 ->back()
                 ->withInput()
                 // TODO: translate
-                ->with('error', 'Couldn\'t create Reply');
+                ->with('error', "Couldn't create Reply");
         }
 
         // Reply note without preview card has been successfully created
         return redirect()->back();
     }
 
-    public function attemptRemoteAction($action)
+    /**
+     * @return mixed|ResponseInterface
+     */
+    public function attemptRemoteAction(string $action)
     {
         $rules = [
             'handle' =>
@@ -228,7 +239,7 @@ class NoteController extends Controller
 
                 $data = get_webfinger_data($username, $domain);
             }
-        } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) {
+        } catch (HTTPException $httpException) {
             return redirect()
                 ->back()
                 ->withInput()
diff --git a/app/Libraries/ActivityPub/Controllers/SchedulerController.php b/app/Libraries/ActivityPub/Controllers/SchedulerController.php
index 617290ae0c..16ff7fdf41 100644
--- a/app/Libraries/ActivityPub/Controllers/SchedulerController.php
+++ b/app/Libraries/ActivityPub/Controllers/SchedulerController.php
@@ -12,9 +12,12 @@ use CodeIgniter\Controller;
 
 class SchedulerController extends Controller
 {
+    /**
+     * @var string[]
+     */
     protected $helpers = ['activitypub'];
 
-    public function activity()
+    public function activity(): void
     {
         // retrieve scheduled activities from database
         $scheduledActivities = model('ActivityModel')->getScheduledActivities();
@@ -24,7 +27,7 @@ class SchedulerController extends Controller
             // send activity to all actor followers
             send_activity_to_followers(
                 $scheduledActivity->actor,
-                json_encode($scheduledActivity->payload),
+                json_encode($scheduledActivity->payload, JSON_THROW_ON_ERROR),
             );
 
             // set activity status to delivered
diff --git a/app/Libraries/ActivityPub/Controllers/WebFingerController.php b/app/Libraries/ActivityPub/Controllers/WebFingerController.php
index df671370d7..0ec6f92ab7 100644
--- a/app/Libraries/ActivityPub/Controllers/WebFingerController.php
+++ b/app/Libraries/ActivityPub/Controllers/WebFingerController.php
@@ -8,19 +8,21 @@
 
 namespace ActivityPub\Controllers;
 
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use ActivityPub\WebFinger;
 use CodeIgniter\Controller;
 use Exception;
 
 class WebFingerController extends Controller
 {
-    public function index()
+    public function index(): ResponseInterface
     {
         try {
             $webfinger = new WebFinger($this->request->getGet('resource'));
-        } catch (Exception $e) {
+        } catch (Exception $exception) {
             // return 404, actor not found
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            throw PageNotFoundException::forPageNotFound();
         }
 
         return $this->response->setJSON($webfinger->toArray());
diff --git a/app/Libraries/ActivityPub/Core/AbstractObject.php b/app/Libraries/ActivityPub/Core/AbstractObject.php
index d25c9db60e..4e4de802d3 100644
--- a/app/Libraries/ActivityPub/Core/AbstractObject.php
+++ b/app/Libraries/ActivityPub/Core/AbstractObject.php
@@ -15,14 +15,17 @@ namespace ActivityPub\Core;
 
 abstract class AbstractObject
 {
-    public function set($property, $value)
+    public function set($property, $value): self
     {
         $this->$property = $value;
 
         return $this;
     }
 
-    public function toArray()
+    /**
+     * @return array<string, string|int|bool|array>
+     */
+    public function toArray(): array
     {
         $objectVars = get_object_vars($this);
         $array = [];
@@ -30,19 +33,22 @@ abstract class AbstractObject
             if ($key === 'context') {
                 $key = '@context';
             }
-            if (is_object($value) && $value instanceof self) {
-                $array[$key] = $value->toArray();
-            } else {
-                $array[$key] = $value;
-            }
+
+            $array[$key] =
+                is_object($value) && $value instanceof self
+                    ? $value->toArray()
+                    : $value;
         }
 
         // removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values
-        return array_filter($array, function ($value) {
+        return array_filter($array, function ($value): bool {
             return $value !== null && $value !== false && $value !== '';
         });
     }
 
+    /**
+     * @return string|bool
+     */
     public function toJSON()
     {
         return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
diff --git a/app/Libraries/ActivityPub/Core/Activity.php b/app/Libraries/ActivityPub/Core/Activity.php
index 5219bbfcdb..ece7239ca1 100644
--- a/app/Libraries/ActivityPub/Core/Activity.php
+++ b/app/Libraries/ActivityPub/Core/Activity.php
@@ -26,7 +26,7 @@ class Activity extends ObjectType
     protected $actor;
 
     /**
-     * @var string|\ActivityPub\Core\ObjectType
+     * @var string|ObjectType
      */
     protected $object;
 }
diff --git a/app/Libraries/ActivityPub/Core/ObjectType.php b/app/Libraries/ActivityPub/Core/ObjectType.php
index 53b865aca5..5ef6ddcd18 100644
--- a/app/Libraries/ActivityPub/Core/ObjectType.php
+++ b/app/Libraries/ActivityPub/Core/ObjectType.php
@@ -48,5 +48,5 @@ class ObjectType extends AbstractObject
     /**
      * @var array
      */
-    protected $cc;
+    protected $cc = [];
 }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php
index 9e2b2f5c98..06cf447b72 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddActors extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -115,7 +115,7 @@ class AddActors extends Migration
         $this->forge->createTable('activitypub_actors');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_actors');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php
index 22f42938c4..f6cc7cf68d 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddNotes extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -101,7 +101,7 @@ class AddNotes extends Migration
         $this->forge->createTable('activitypub_notes');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_notes');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php
index 11555bf1a0..de475d92ca 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddActivities extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -83,7 +83,7 @@ class AddActivities extends Migration
         $this->forge->createTable('activitypub_activities');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_activities');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php
index d76d54523a..c7ecddcfb0 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddFavourites extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'actor_id' => [
@@ -48,7 +48,7 @@ class AddFavourites extends Migration
         $this->forge->createTable('activitypub_favourites');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_favourites');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php
index f3145f4f3c..b7b8a809df 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddFollowers extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'actor_id' => [
@@ -50,7 +50,7 @@ class AddFollowers extends Migration
         $this->forge->createTable('activitypub_follows');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_follows');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php
index eaa07eb47c..978572ffa6 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPreviewCards extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -75,7 +75,7 @@ class AddPreviewCards extends Migration
         $this->forge->createTable('activitypub_preview_cards');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_preview_cards');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php
index 25fd22f9f7..74ae7dbc3f 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddNotesPreviewCards extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'note_id' => [
@@ -46,7 +46,7 @@ class AddNotesPreviewCards extends Migration
         $this->forge->createTable('activitypub_notes_preview_cards');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_notes_preview_cards');
     }
diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php
index 3b136dca55..7f7488d2ac 100644
--- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php
+++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddBlockedDomains extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'name' => [
@@ -30,7 +30,7 @@ class AddBlockedDomains extends Migration
         $this->forge->createTable('activitypub_blocked_domains');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('activitypub_blocked_domains');
     }
diff --git a/app/Libraries/ActivityPub/Entities/Activity.php b/app/Libraries/ActivityPub/Entities/Activity.php
index 14a7ed0dd4..fa595cec4e 100644
--- a/app/Libraries/ActivityPub/Entities/Activity.php
+++ b/app/Libraries/ActivityPub/Entities/Activity.php
@@ -8,29 +8,39 @@
 
 namespace ActivityPub\Entities;
 
+use RuntimeException;
 use Michalsn\Uuid\UuidEntity;
 
 class Activity extends UuidEntity
 {
+    /**
+     * @var string[]
+     */
     protected $uuids = ['id', 'note_id'];
 
     /**
-     * @var \ActivityPub\Entities\Actor
+     * @var Actor
      */
     protected $actor;
 
     /**
-     * @var \ActivityPub\Entities\Actor
+     * @var Actor
      */
     protected $target_actor;
 
     /**
-     * @var \ActivityPub\Entities\Note
+     * @var Note
      */
     protected $note;
 
+    /**
+     * @var string[]
+     */
     protected $dates = ['scheduled_at', 'created_at'];
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'string',
         'actor_id' => 'integer',
@@ -41,13 +51,10 @@ class Activity extends UuidEntity
         'status' => '?string',
     ];
 
-    /**
-     * @return \ActivityPub\Entities\Actor
-     */
-    public function getActor()
+    public function getActor(): Actor
     {
         if (empty($this->actor_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Activity must have an actor_id before getting the actor.',
             );
         }
@@ -59,13 +66,10 @@ class Activity extends UuidEntity
         return $this->actor;
     }
 
-    /**
-     * @return \ActivityPub\Entities\Actor
-     */
-    public function getTargetActor()
+    public function getTargetActor(): Actor
     {
         if (empty($this->target_actor_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Activity must have a target_actor_id before getting the target actor.',
             );
         }
@@ -79,13 +83,10 @@ class Activity extends UuidEntity
         return $this->target_actor;
     }
 
-    /**
-     * @return \ActivityPub\Entities\Note
-     */
-    public function getNote()
+    public function getNote(): Note
     {
         if (empty($this->note_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Activity must have a note_id before getting note.',
             );
         }
diff --git a/app/Libraries/ActivityPub/Entities/Actor.php b/app/Libraries/ActivityPub/Entities/Actor.php
index acf73aa31e..db64f83718 100644
--- a/app/Libraries/ActivityPub/Entities/Actor.php
+++ b/app/Libraries/ActivityPub/Entities/Actor.php
@@ -8,7 +8,8 @@
 
 namespace ActivityPub\Entities;
 
-use CodeIgniter\Entity;
+use RuntimeException;
+use CodeIgniter\Entity\Entity;
 
 class Actor extends Entity
 {
@@ -18,15 +19,18 @@ class Actor extends Entity
     protected $key_id;
 
     /**
-     * @var \ActivityPub\Entities\Actor[]
+     * @var Actor[]
      */
-    protected $followers;
+    protected $followers = [];
 
     /**
      * @var boolean
      */
-    protected $is_local;
+    protected $is_local = false;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'uri' => 'string',
@@ -48,12 +52,12 @@ class Actor extends Entity
         'is_blocked' => 'boolean',
     ];
 
-    public function getKeyId()
+    public function getKeyId(): string
     {
         return $this->uri . '#main-key';
     }
 
-    public function getIsLocal()
+    public function getIsLocal(): bool
     {
         if (!$this->is_local) {
             $uri = current_url(true);
@@ -67,22 +71,27 @@ class Actor extends Entity
         return $this->is_local;
     }
 
-    public function getFollowers()
+    /**
+     * @return Follower[]
+     */
+    public function getFollowers(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Actor must be created before getting followers.',
             );
         }
 
         if (empty($this->followers)) {
-            $this->followers = model('ActorModel')->getFollowers($this->id);
+            $this->followers = (array) model('ActorModel')->getFollowers(
+                $this->id,
+            );
         }
 
         return $this->followers;
     }
 
-    public function getAvatarImageUrl()
+    public function getAvatarImageUrl(): string
     {
         if (empty($this->attributes['avatar_image_url'])) {
             return base_url(config('ActivityPub')->defaultAvatarImagePath);
@@ -91,7 +100,7 @@ class Actor extends Entity
         return $this->attributes['avatar_image_url'];
     }
 
-    public function getAvatarImageMimetype()
+    public function getAvatarImageMimetype(): string
     {
         if (empty($this->attributes['avatar_image_mimetype'])) {
             return config('ActivityPub')->defaultAvatarImageMimetype;
@@ -100,7 +109,7 @@ class Actor extends Entity
         return $this->attributes['avatar_image_mimetype'];
     }
 
-    public function getCoverImageUrl()
+    public function getCoverImageUrl(): string
     {
         if (empty($this->attributes['cover_image_url'])) {
             return base_url(config('ActivityPub')->defaultCoverImagePath);
@@ -109,7 +118,7 @@ class Actor extends Entity
         return $this->attributes['cover_image_url'];
     }
 
-    public function getCoverImageMimetype()
+    public function getCoverImageMimetype(): string
     {
         if (empty($this->attributes['cover_image_mimetype'])) {
             return config('ActivityPub')->defaultCoverImageMimetype;
diff --git a/app/Libraries/ActivityPub/Entities/BlockedDomain.php b/app/Libraries/ActivityPub/Entities/BlockedDomain.php
index bf609e7cb4..8405d88308 100644
--- a/app/Libraries/ActivityPub/Entities/BlockedDomain.php
+++ b/app/Libraries/ActivityPub/Entities/BlockedDomain.php
@@ -8,10 +8,13 @@
 
 namespace ActivityPub\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class BlockedDomain extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'name' => 'string',
     ];
diff --git a/app/Libraries/ActivityPub/Entities/Favourite.php b/app/Libraries/ActivityPub/Entities/Favourite.php
index 759448d433..ea4381b274 100644
--- a/app/Libraries/ActivityPub/Entities/Favourite.php
+++ b/app/Libraries/ActivityPub/Entities/Favourite.php
@@ -12,8 +12,14 @@ use Michalsn\Uuid\UuidEntity;
 
 class Favourite extends UuidEntity
 {
+    /**
+     * @var string[]
+     */
     protected $uuids = ['note_id'];
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'actor_id' => 'integer',
         'note_id' => 'integer',
diff --git a/app/Libraries/ActivityPub/Entities/Follow.php b/app/Libraries/ActivityPub/Entities/Follow.php
index dea45eed6c..fd966a1772 100644
--- a/app/Libraries/ActivityPub/Entities/Follow.php
+++ b/app/Libraries/ActivityPub/Entities/Follow.php
@@ -8,10 +8,13 @@
 
 namespace ActivityPub\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class Follow extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'actor_id' => 'integer',
         'target_actor_id' => 'integer',
diff --git a/app/Libraries/ActivityPub/Entities/Note.php b/app/Libraries/ActivityPub/Entities/Note.php
index af278d4b91..b6b6d858e3 100644
--- a/app/Libraries/ActivityPub/Entities/Note.php
+++ b/app/Libraries/ActivityPub/Entities/Note.php
@@ -8,64 +8,74 @@
 
 namespace ActivityPub\Entities;
 
+use RuntimeException;
 use Michalsn\Uuid\UuidEntity;
 
 class Note extends UuidEntity
 {
+    /**
+     * @var string[]
+     */
     protected $uuids = ['id', 'in_reply_to_id', 'reblog_of_id'];
 
     /**
-     * @var \ActivityPub\Entities\Actor
+     * @var Actor
      */
     protected $actor;
 
     /**
      * @var boolean
      */
-    protected $is_reply;
+    protected $is_reply = false;
 
     /**
-     * @var \ActivityPub\Entities\Note
+     * @var Note
      */
     protected $reply_to_note;
 
     /**
      * @var boolean
      */
-    protected $is_reblog;
+    protected $is_reblog = false;
 
     /**
-     * @var \ActivityPub\Entities\Note
+     * @var Note
      */
     protected $reblog_of_note;
 
     /**
-     * @var \ActivityPub\Entities\PreviewCard
+     * @var PreviewCard|null
      */
     protected $preview_card;
 
     /**
      * @var boolean
      */
-    protected $has_preview_card;
+    protected $has_preview_card = false;
 
     /**
-     * @var \ActivityPub\Entities\Note[]
+     * @var Note[]
      */
-    protected $replies;
+    protected $replies = [];
 
     /**
      * @var boolean
      */
-    protected $has_replies;
+    protected $has_replies = false;
 
     /**
-     * @var \ActivityPub\Entities\Note[]
+     * @var Note[]
      */
-    protected $reblogs;
+    protected $reblogs = [];
 
+    /**
+     * @var string[]
+     */
     protected $dates = ['published_at', 'created_at'];
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'string',
         'uri' => 'string',
@@ -81,13 +91,11 @@ class Note extends UuidEntity
 
     /**
      * Returns the note's actor
-     *
-     * @return \ActivityPub\Entities\Actor
      */
-    public function getActor()
+    public function getActor(): Actor
     {
         if (empty($this->actor_id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Note must have an actor_id before getting actor.',
             );
         }
@@ -99,10 +107,10 @@ class Note extends UuidEntity
         return $this->actor;
     }
 
-    public function getPreviewCard()
+    public function getPreviewCard(): ?PreviewCard
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Note must be created before getting preview_card.',
             );
         }
@@ -116,42 +124,47 @@ class Note extends UuidEntity
         return $this->preview_card;
     }
 
-    public function getHasPreviewCard()
+    public function getHasPreviewCard(): bool
     {
-        return !empty($this->getPreviewCard()) ? true : false;
+        return !empty($this->getPreviewCard());
     }
 
-    public function getIsReply()
+    public function getIsReply(): bool
     {
         $this->is_reply = $this->in_reply_to_id !== null;
 
         return $this->is_reply;
     }
 
-    public function getReplies()
+    /**
+     * @return Note[]
+     */
+    public function getReplies(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Note must be created before getting replies.',
             );
         }
 
         if (empty($this->replies)) {
-            $this->replies = model('NoteModel')->getNoteReplies($this->id);
+            $this->replies = (array) model('NoteModel')->getNoteReplies(
+                $this->id,
+            );
         }
 
         return $this->replies;
     }
 
-    public function getHasReplies()
+    public function getHasReplies(): bool
     {
-        return !empty($this->getReplies()) ? true : false;
+        return !empty($this->getReplies());
     }
 
-    public function getReplyToNote()
+    public function getReplyToNote(): Note
     {
         if (empty($this->in_reply_to_id)) {
-            throw new \RuntimeException('Note is not a reply.');
+            throw new RuntimeException('Note is not a reply.');
         }
 
         if (empty($this->reply_to_note)) {
@@ -163,30 +176,35 @@ class Note extends UuidEntity
         return $this->reply_to_note;
     }
 
-    public function getReblogs()
+    /**
+     * @return Note[]
+     */
+    public function getReblogs(): array
     {
         if (empty($this->id)) {
-            throw new \RuntimeException(
+            throw new RuntimeException(
                 'Note must be created before getting reblogs.',
             );
         }
 
         if (empty($this->reblogs)) {
-            $this->reblogs = model('NoteModel')->getNoteReblogs($this->id);
+            $this->reblogs = (array) model('NoteModel')->getNoteReblogs(
+                $this->id,
+            );
         }
 
         return $this->reblogs;
     }
 
-    public function getIsReblog()
+    public function getIsReblog(): bool
     {
         return $this->reblog_of_id != null;
     }
 
-    public function getReblogOfNote()
+    public function getReblogOfNote(): Note
     {
         if (empty($this->reblog_of_id)) {
-            throw new \RuntimeException('Note is not a reblog.');
+            throw new RuntimeException('Note is not a reblog.');
         }
 
         if (empty($this->reblog_of_note)) {
@@ -198,7 +216,7 @@ class Note extends UuidEntity
         return $this->reblog_of_note;
     }
 
-    public function setMessage(string $message)
+    public function setMessage(string $message): self
     {
         helper('activitypub');
 
diff --git a/app/Libraries/ActivityPub/Entities/PreviewCard.php b/app/Libraries/ActivityPub/Entities/PreviewCard.php
index cd3523c460..62cbcd6212 100644
--- a/app/Libraries/ActivityPub/Entities/PreviewCard.php
+++ b/app/Libraries/ActivityPub/Entities/PreviewCard.php
@@ -8,10 +8,13 @@
 
 namespace ActivityPub\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class PreviewCard extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'id' => 'integer',
         'note_id' => 'string',
diff --git a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php
index bf9d870838..325035432e 100644
--- a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php
+++ b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php
@@ -2,6 +2,9 @@
 
 namespace ActivityPub\Filters;
 
+use Config\Services;
+use CodeIgniter\Exceptions\PageNotFoundException;
+use Exception;
 use ActivityPub\HttpSignature;
 use CodeIgniter\HTTP\RequestInterface;
 use CodeIgniter\HTTP\ResponseInterface;
@@ -20,10 +23,8 @@ class ActivityPubFilter implements FilterInterface
      * sent back to the client, allowing for error pages,
      * redirects, etc.
      *
-     * @param \CodeIgniter\HTTP\RequestInterface $request
      * @param array|null                         $params
-     *
-     * @return mixed
+     * @return void|mixed
      */
     public function before(RequestInterface $request, $params = null)
     {
@@ -32,7 +33,7 @@ class ActivityPubFilter implements FilterInterface
         }
 
         if (in_array('verify-activitystream', $params)) {
-            $negotiate = \Config\Services::negotiator();
+            $negotiate = Services::negotiator();
 
             $allowedContentTypes = [
                 'application/ld+json; profile="https://www.w3.org/ns/activitystreams',
@@ -41,7 +42,7 @@ class ActivityPubFilter implements FilterInterface
 
             if (empty($negotiate->media($allowedContentTypes))) {
                 // return $this->response->setStatusCode(415)->setJSON([]);
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+                throw PageNotFoundException::forPageNotFound();
             }
         }
 
@@ -53,12 +54,12 @@ class ActivityPubFilter implements FilterInterface
 
             // check first if domain is blocked
             if (model('BlockedDomainModel')->isDomainBlocked($domain)) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+                throw PageNotFoundException::forPageNotFound();
             }
 
             // check if actor is blocked
             if (model('ActorModel')->isActorBlocked($actorUri)) {
-                throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+                throw PageNotFoundException::forPageNotFound();
             }
         }
 
@@ -66,7 +67,7 @@ class ActivityPubFilter implements FilterInterface
             try {
                 // securityCheck: check activity signature before handling it
                 (new HttpSignature())->verify();
-            } catch (\Exception $e) {
+            } catch (Exception $exception) {
                 // Invalid HttpSignature (401 = unauthorized)
                 // TODO: show error message?
                 return service('response')->setStatusCode(401);
@@ -75,24 +76,19 @@ class ActivityPubFilter implements FilterInterface
     }
 
     //--------------------------------------------------------------------
-
     /**
      * Allows After filters to inspect and modify the response
      * object as needed. This method does not allow any way
      * to stop execution of other after filters, short of
      * throwing an Exception or Error.
      *
-     * @param \CodeIgniter\HTTP\RequestInterface  $request
-     * @param \CodeIgniter\HTTP\ResponseInterface $response
      * @param array|null                          $arguments
-     *
-     * @return void
      */
     public function after(
         RequestInterface $request,
         ResponseInterface $response,
         $arguments = null
-    ) {
+    ): void {
     }
 
     //--------------------------------------------------------------------
diff --git a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php
index b42273718c..8e6e6d6e19 100644
--- a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php
+++ b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php
@@ -6,6 +6,11 @@
  * @link       https://castopod.org/
  */
 
+use CodeIgniter\HTTP\URI;
+use Config\Database;
+use Essence\Essence;
+use ActivityPub\Entities\PreviewCard;
+use ActivityPub\Entities\Actor;
 use ActivityPub\Activities\AcceptActivity;
 use ActivityPub\ActivityRequest;
 use CodeIgniter\HTTP\Exceptions\HTTPException;
@@ -14,15 +19,11 @@ if (!function_exists('get_webfinger_data')) {
     /**
      * Retrieve actor webfinger data from username and domain
      *
-     * @param string $username
-     * @param string $domain
-     * @return mixed
-     * @throws HTTPException
-     * @throws InvalidArgumentException
+     * @return object|null
      */
-    function get_webfinger_data($username, $domain)
+    function get_webfinger_data(string $username, string $domain): ?object
     {
-        $webfingerUri = new \CodeIgniter\HTTP\URI();
+        $webfingerUri = new URI();
         $webfingerUri->setScheme('https');
         $webfingerUri->setHost($domain);
         isset($port) && $webfingerUri->setPort((int) $port);
@@ -32,7 +33,12 @@ if (!function_exists('get_webfinger_data')) {
         $webfingerRequest = new ActivityRequest($webfingerUri);
         $webfingerResponse = $webfingerRequest->get();
 
-        return json_decode($webfingerResponse->getBody());
+        return json_decode(
+            $webfingerResponse->getBody(),
+            null,
+            512,
+            JSON_THROW_ON_ERROR,
+        );
     }
 }
 
@@ -47,7 +53,7 @@ if (!function_exists('split_handle')) {
     {
         if (
             !preg_match(
-                '/^@?(?P<username>[\w\.\-]+)@(?P<domain>[\w\.\-]+)(?P<port>:[\d]+)?$/',
+                '~^@?(?P<username>[\w\.\-]+)@(?P<domain>[\w\.\-]+)(?P<port>:[\d]+)?$~',
                 $handle,
                 $matches,
             )
@@ -63,17 +69,18 @@ if (!function_exists('accept_follow')) {
     /**
      * Sends an accept activity to the targetActor's inbox
      *
-     * @param \ActivityPub\Entities\Actor $actor Actor which accepts the follow
-     * @param \ActivityPub\Entities\Actor $targetActor Actor which receives the accept follow
-     * @param string $objectId
-     * @return void
+     * @param Actor $actor Actor which accepts the follow
+     * @param Actor $targetActor Actor which receives the accept follow
      */
-    function accept_follow($actor, $targetActor, $objectId)
-    {
+    function accept_follow(
+        Actor $actor,
+        Actor $targetActor,
+        string $objectId
+    ): void {
         $acceptActivity = new AcceptActivity();
         $acceptActivity->set('actor', $actor->uri)->set('object', $objectId);
 
-        $db = \Config\Database::connect();
+        $db = Database::connect();
         $db->transStart();
 
         $activityModel = model('ActivityModel');
@@ -101,7 +108,7 @@ if (!function_exists('accept_follow')) {
             );
             $acceptRequest->sign($actor->key_id, $actor->private_key);
             $acceptRequest->post();
-        } catch (\Exception $e) {
+        } catch (Exception $exception) {
             $db->transRollback();
         }
 
@@ -113,11 +120,9 @@ if (!function_exists('send_activity_to_followers')) {
     /**
      * Sends an activity to all actor followers
      *
-     * @param \ActivityPub\Entities\Actor $actor
      * @param string $activity
-     * @return void
      */
-    function send_activity_to_followers($actor, $activityPayload)
+    function send_activity_to_followers(Actor $actor, $activityPayload): void
     {
         foreach ($actor->followers as $follower) {
             try {
@@ -127,7 +132,7 @@ if (!function_exists('send_activity_to_followers')) {
                 );
                 $acceptRequest->sign($actor->key_id, $actor->private_key);
                 $acceptRequest->post();
-            } catch (\Exception $e) {
+            } catch (Exception $e) {
                 // log error
                 log_message('critical', $e);
             }
@@ -139,10 +144,9 @@ if (!function_exists('extract_urls_from_message')) {
     /**
      * Returns an array of all urls from a string
      *
-     * @param mixed $message
      * @return string[]
      */
-    function extract_urls_from_message($message)
+    function extract_urls_from_message(string $message): array
     {
         preg_match_all(
             '~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i',
@@ -158,12 +162,11 @@ if (!function_exists('create_preview_card_from_url')) {
     /**
      * Extract open graph metadata from given url and create preview card
      *
-     * @param \CodeIgniter\HTTP\URI $url
-     * @return \ActivityPub\Entities\PreviewCard|null
+     * @return PreviewCard|null
      */
-    function create_preview_card_from_url($url)
+    function create_preview_card_from_url(URI $url): ?PreviewCard
     {
-        $essence = new \Essence\Essence([
+        $essence = new Essence([
             'filters' => [
                 'OEmbedProvider' => '//',
                 'OpenGraphProvider' => '//',
@@ -182,7 +185,7 @@ if (!function_exists('create_preview_card_from_url')) {
 
             // Check that, at least, the url and title are set
             if ($media->url && $media->title) {
-                $preview_card = new \ActivityPub\Entities\PreviewCard([
+                $preview_card = new PreviewCard([
                     'url' => (string) $url,
                     'title' => $media->title,
                     'description' => $media->description,
@@ -219,10 +222,9 @@ if (!function_exists('get_or_create_preview_card_from_url')) {
     /**
      * Extract open graph metadata from given url and create preview card
      *
-     * @param \CodeIgniter\HTTP\URI $url
-     * @return \ActivityPub\Entities\PreviewCard|null
+     * @return PreviewCard|null
      */
-    function get_or_create_preview_card_from_url($url)
+    function get_or_create_preview_card_from_url(URI $url): ?PreviewCard
     {
         // check if preview card has already been generated
         if (
@@ -243,10 +245,9 @@ if (!function_exists('get_or_create_actor_from_uri')) {
      * Retrieves actor from database using the actor uri
      * If Actor is not present, it creates the record in the database and returns it.
      *
-     * @param string $actorUri
-     * @return \ActivityPub\Entities\Actor|null
+     * @return Actor|null
      */
-    function get_or_create_actor_from_uri($actorUri)
+    function get_or_create_actor_from_uri(string $actorUri): ?Actor
     {
         // check if actor exists in database already and return it
         if ($actor = model('ActorModel')->getActorByUri($actorUri)) {
@@ -263,11 +264,9 @@ if (!function_exists('get_or_create_actor')) {
      * Retrieves actor from database using the actor username and domain
      * If actor is not present, it creates the record in the database and returns it.
      *
-     * @param string $username
-     * @param string $domain
-     * @return \ActivityPub\Entities\Actor|null
+     * @return Actor|null
      */
-    function get_or_create_actor($username, $domain)
+    function get_or_create_actor(string $username, string $domain): ?Actor
     {
         // check if actor exists in database already and return it
         if (
@@ -292,16 +291,20 @@ if (!function_exists('create_actor_from_uri')) {
      * Creates actor record in database using
      * the info gathered from the actorUri parameter
      *
-     * @param string $actorUri
-     * @return \ActivityPub\Entities\Actor|null
+     * @return Actor|null
      */
-    function create_actor_from_uri($actorUri)
+    function create_actor_from_uri(string $actorUri): ?Actor
     {
         $activityRequest = new ActivityRequest($actorUri);
         $actorResponse = $activityRequest->get();
-        $actorPayload = json_decode($actorResponse->getBody());
+        $actorPayload = json_decode(
+            $actorResponse->getBody(),
+            null,
+            512,
+            JSON_THROW_ON_ERROR,
+        );
 
-        $newActor = new \ActivityPub\Entities\Actor();
+        $newActor = new Actor();
         $newActor->uri = $actorUri;
         $newActor->username = $actorPayload->preferredUsername;
         $newActor->domain = $activityRequest->getDomain();
@@ -335,10 +338,9 @@ if (!function_exists('get_current_domain')) {
     /**
      * Returns instance's domain name
      *
-     * @return string
      * @throws HTTPException
      */
-    function get_current_domain()
+    function get_current_domain(): string
     {
         $uri = current_url(true);
         return $uri->getHost() . ($uri->getPort() ? ':' . $uri->getPort() : '');
@@ -349,12 +351,11 @@ if (!function_exists('extract_text_from_html')) {
     /**
      * Extracts the text from html content
      *
-     * @param mixed $content
      * @return string|string[]|null
      */
-    function extract_text_from_html($content)
+    function extract_text_from_html(string $content)
     {
-        return preg_replace('/\s+/', ' ', strip_tags($content));
+        return preg_replace('~\s+~', ' ', strip_tags($content));
     }
 }
 
@@ -366,14 +367,13 @@ if (!function_exists('linkify')) {
      * @param string $value
      * @param array  $protocols  http/https, ftp, mail, twitter
      * @param array  $attributes
-     * @return string
      */
-    function linkify($text, $protocols = ['http', 'handle'])
+    function linkify($text, array $protocols = ['http', 'handle']): string
     {
         $links = [];
 
         // Extract text links for each protocol
-        foreach ((array) $protocols as $protocol) {
+        foreach ($protocols as $protocol) {
             switch ($protocol) {
                 case 'http':
                 case 'https':
@@ -388,7 +388,7 @@ if (!function_exists('linkify')) {
                             helper('text');
 
                             $link = preg_replace(
-                                '#^www\.(.+\.)#i',
+                                '~^www\.(.+\.)~i',
                                 '$1',
                                 $link,
                             );
@@ -397,7 +397,7 @@ if (!function_exists('linkify')) {
                                 array_push(
                                     $links,
                                     anchor(
-                                        "$protocol://$link",
+                                        "{$protocol}://{$link}",
                                         ellipsize(rtrim($link, '/'), 30),
                                         [
                                             'target' => '_blank',
@@ -434,28 +434,27 @@ if (!function_exists('linkify')) {
                                             ]),
                                         ) .
                                         '>';
-                                } else {
-                                    try {
-                                        $actor = get_or_create_actor(
-                                            $match['username'],
-                                            $match['domain'],
-                                        );
-                                        return '<' .
-                                            array_push(
-                                                $links,
-                                                anchor($actor->uri, $match[0], [
-                                                    'target' => '_blank',
-                                                    'rel' =>
-                                                        'noopener noreferrer',
-                                                ]),
-                                            ) .
-                                            '>';
-                                    } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) {
-                                        // Couldn't retrieve actor, do not wrap the text in link
-                                        return '<' .
-                                            array_push($links, $match[0]) .
-                                            '>';
-                                    }
+                                }
+
+                                try {
+                                    $actor = get_or_create_actor(
+                                        $match['username'],
+                                        $match['domain'],
+                                    );
+                                    return '<' .
+                                        array_push(
+                                            $links,
+                                            anchor($actor->uri, $match[0], [
+                                                'target' => '_blank',
+                                                'rel' => 'noopener noreferrer',
+                                            ]),
+                                        ) .
+                                        '>';
+                                } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $httpException) {
+                                    // Couldn't retrieve actor, do not wrap the text in link
+                                    return '<' .
+                                        array_push($links, $match[0]) .
+                                        '>';
                                 }
                             } else {
                                 if (
@@ -488,10 +487,14 @@ if (!function_exists('linkify')) {
                             return '<' .
                                 array_push(
                                     $links,
-                                    anchor("$protocol://$match[1]", $match[1], [
-                                        'target' => '_blank',
-                                        'rel' => 'noopener noreferrer',
-                                    ]),
+                                    anchor(
+                                        "{$protocol}://$match[1]",
+                                        $match[1],
+                                        [
+                                            'target' => '_blank',
+                                            'rel' => 'noopener noreferrer',
+                                        ],
+                                    ),
                                 ) .
                                 '>';
                         },
@@ -503,7 +506,7 @@ if (!function_exists('linkify')) {
 
         // Insert all links
         return preg_replace_callback(
-            '/<(\d+)>/',
+            '~<(\d+)>~',
             function ($match) use (&$links) {
                 return $links[$match[1] - 1];
             },
diff --git a/app/Libraries/ActivityPub/HttpSignature.php b/app/Libraries/ActivityPub/HttpSignature.php
index 8b1bde6dbe..077233fd05 100644
--- a/app/Libraries/ActivityPub/HttpSignature.php
+++ b/app/Libraries/ActivityPub/HttpSignature.php
@@ -24,6 +24,9 @@ use phpseclib\Crypt\RSA;
  */
 class HttpSignature
 {
+    /**
+     * @var string
+     */
     const SIGNATURE_PATTERN = '/^
         keyId="(?P<keyId>
             (https?:\/\/[\w\-\.]+[\w]+)
@@ -31,18 +34,15 @@ class HttpSignature
             ([\w\-\.#\/@]+)
         )",
         algorithm="(?P<algorithm>[\w\-]+)",
-        (headers="\(request-target\) (?P<headers>[\w\-\s]+)",)?
+        (headers="\(request-target\) (?P<headers>[\w\\-\s]+)",)?
         signature="(?P<signature>[\w+\/]+={0,2})"
     /x';
 
     /**
-     * @var \CodeIgniter\HTTP\IncomingRequest
+     * @var IncomingRequest
      */
     protected $request;
 
-    /**
-     * @param \CodeIgniter\HTTP\IncomingRequest $request
-     */
     public function __construct(IncomingRequest $request = null)
     {
         if (is_null($request)) {
@@ -57,7 +57,7 @@ class HttpSignature
      *
      * @return bool True if signature has been verified. Otherwise false
      */
-    public function verify()
+    public function verify(): bool
     {
         if (!($dateHeader = $this->request->header('date'))) {
             throw new Exception('Request must include a date header.');
@@ -87,7 +87,7 @@ class HttpSignature
         }
 
         // read the Signature header
-        if (!($signature = $this->request->getHeaderLine('signature'))) {
+        if (($signature = $this->request->getHeaderLine('signature')) === '') {
             // Signature header not found
             throw new Exception('Request must include a signature header');
         }
@@ -103,7 +103,12 @@ class HttpSignature
         // Fetch the public key linked from keyId
         $actorRequest = new ActivityRequest($keyId);
         $actorResponse = $actorRequest->get();
-        $actor = json_decode($actorResponse->getBody());
+        $actor = json_decode(
+            $actorResponse->getBody(),
+            null,
+            512,
+            JSON_THROW_ON_ERROR,
+        );
 
         $publicKeyPem = $actor->publicKey->publicKeyPem;
 
@@ -123,8 +128,7 @@ class HttpSignature
     /**
      * Split HTTP signature into its parts (keyId, headers and signature)
      *
-     * @param string $signature
-     * @return bool|array
+     * @return bool|mixed
      */
     private function splitSignature(string $signature)
     {
@@ -145,23 +149,22 @@ class HttpSignature
      * Get plain text that has been originally signed
      *
      * @param  array $headers HTTP header keys
-     * @return string
      */
-    private function getPlainText(array $headers)
+    private function getPlainText(array $headers): string
     {
         $strings = [];
         $strings[] = sprintf(
             '(request-target): %s %s%s',
             $this->request->getMethod(),
             '/' . $this->request->uri->getPath(),
-            $this->request->uri->getQuery()
+            $this->request->uri->getQuery() !== ''
                 ? '?' . $this->request->uri->getQuery()
                 : '',
         );
 
         foreach ($headers as $key) {
             if ($this->request->hasHeader($key)) {
-                $strings[] = "$key: {$this->request->getHeaderLine($key)}";
+                $strings[] = "{$key}: {$this->request->getHeaderLine($key)}";
             }
         }
 
diff --git a/app/Libraries/ActivityPub/Models/ActivityModel.php b/app/Libraries/ActivityPub/Models/ActivityModel.php
index 0d7a82ad5d..cdd9020e74 100644
--- a/app/Libraries/ActivityPub/Models/ActivityModel.php
+++ b/app/Libraries/ActivityPub/Models/ActivityModel.php
@@ -8,15 +8,30 @@
 
 namespace ActivityPub\Models;
 
+use ActivityPub\Entities\Activity;
+use CodeIgniter\I18n\Time;
+use DateTimeInterface;
 use Michalsn\Uuid\UuidModel;
 
 class ActivityModel extends UuidModel
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_activities';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $uuidFields = ['id', 'note_id'];
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'actor_id',
@@ -28,11 +43,20 @@ class ActivityModel extends UuidModel
         'scheduled_at',
     ];
 
-    protected $returnType = \ActivityPub\Entities\Activity::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Activity::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
-    protected $updatedField = null;
+    protected $updatedField;
 
     public function getActivityById($activityId)
     {
@@ -50,24 +74,18 @@ class ActivityModel extends UuidModel
     /**
      * Inserts a new activity record in the database
      *
-     * @param string $type
-     * @param integer $actorId
-     * @param integer $targetActorId
-     * @param integer $noteId
-     * @param string $payload
-     * @param \CodeIgniter\I18n\Time $scheduledAt
-     * @param string $status
+     * @param Time $scheduledAt
      *
      * @return Michalsn\Uuid\BaseResult|int|string|false
      */
     public function newActivity(
-        $type,
-        $actorId,
-        $targetActorId,
-        $noteId,
-        $payload,
-        $scheduledAt = null,
-        $status = null
+        string $type,
+        int $actorId,
+        ?int $targetActorId,
+        ?string $noteId,
+        string $payload,
+        DateTimeInterface $scheduledAt = null,
+        ?string $status = null
     ) {
         return $this->insert(
             [
diff --git a/app/Libraries/ActivityPub/Models/ActorModel.php b/app/Libraries/ActivityPub/Models/ActorModel.php
index a82ee94e73..c7548d8f47 100644
--- a/app/Libraries/ActivityPub/Models/ActorModel.php
+++ b/app/Libraries/ActivityPub/Models/ActorModel.php
@@ -8,13 +8,20 @@
 
 namespace ActivityPub\Models;
 
+use ActivityPub\Entities\Actor;
 use CodeIgniter\Events\Events;
 use CodeIgniter\Model;
 
 class ActorModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_actors';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'uri',
@@ -36,9 +43,18 @@ class ActorModel extends Model
         'is_blocked',
     ];
 
-    protected $returnType = \ActivityPub\Entities\Actor::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Actor::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
     public function getActorById($id)
@@ -56,13 +72,11 @@ class ActorModel extends Model
     /**
      * Looks for actor with username and domain,
      * if no domain has been specified, the current host will be used
-     *
-     * @param mixed $username
-     * @param mixed|null $domain
-     * @return mixed
      */
-    public function getActorByUsername($username, $domain = null)
-    {
+    public function getActorByUsername(
+        string $username,
+        ?string $domain = null
+    ): ?Actor {
         // TODO: is there a better way?
         helper('activitypub');
 
@@ -70,13 +84,14 @@ class ActorModel extends Model
             $domain = get_current_domain();
         }
 
-        if (!($found = cache("actor@{$username}@{$domain}"))) {
+        $cacheName = "actor-{$username}-{$domain}";
+        if (!($found = cache($cacheName))) {
             $found = $this->where([
                 'username' => $username,
                 'domain' => $domain,
             ])->first();
 
-            cache()->save("actor@{$username}@{$domain}", $found, DECADE);
+            cache()->save($cacheName, $found, DECADE);
         }
 
         return $found;
@@ -86,7 +101,7 @@ class ActorModel extends Model
     {
         $hashedActorUri = md5($actorUri);
         $cacheName =
-            config('ActivityPub')->cachePrefix . "actor@{$hashedActorUri}";
+            config('ActivityPub')->cachePrefix . "actor-{$hashedActorUri}";
         if (!($found = cache($cacheName))) {
             $found = $this->where('uri', $actorUri)->first();
 
@@ -118,11 +133,8 @@ class ActorModel extends Model
     /**
      * Check if an existing actor is blocked using its uri.
      * Returns FALSE if the actor doesn't exist
-     *
-     * @param mixed $actorUri
-     * @return boolean
      */
-    public function isActorBlocked($actorUri)
+    public function isActorBlocked(string $actorUri): bool
     {
         if ($actor = $this->getActorByUri($actorUri)) {
             return $actor->is_blocked;
@@ -134,9 +146,9 @@ class ActorModel extends Model
     /**
      * Retrieves all blocked actors.
      *
-     * @return \ActivityPub\Entities\Actor[]
+     * @return Actor[]
      */
-    public function getBlockedActors()
+    public function getBlockedActors(): array
     {
         $cacheName = config('ActivityPub')->cachePrefix . 'blocked_actors';
         if (!($found = cache($cacheName))) {
@@ -148,7 +160,7 @@ class ActorModel extends Model
         return $found;
     }
 
-    public function blockActor($actorId)
+    public function blockActor($actorId): void
     {
         $prefix = config('ActivityPub')->cachePrefix;
         cache()->delete($prefix . 'blocked_actors');
@@ -159,7 +171,7 @@ class ActorModel extends Model
         $this->update($actorId, ['is_blocked' => 1]);
     }
 
-    public function unblockActor($actorId)
+    public function unblockActor($actorId): void
     {
         $prefix = config('ActivityPub')->cachePrefix;
         cache()->delete($prefix . 'blocked_actors');
diff --git a/app/Libraries/ActivityPub/Models/BlockedDomainModel.php b/app/Libraries/ActivityPub/Models/BlockedDomainModel.php
index d87e115749..60b0f20380 100644
--- a/app/Libraries/ActivityPub/Models/BlockedDomainModel.php
+++ b/app/Libraries/ActivityPub/Models/BlockedDomainModel.php
@@ -8,21 +8,44 @@
 
 namespace ActivityPub\Models;
 
+use CodeIgniter\Database\BaseResult;
+use ActivityPub\Entities\BlockedDomain;
 use CodeIgniter\Events\Events;
 use CodeIgniter\Model;
 
 class BlockedDomainModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_blocked_domains';
+
+    /**
+     * @var string
+     */
     protected $primaryKey = 'name';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['name'];
 
-    protected $returnType = \ActivityPub\Entities\BlockedDomain::class;
+    /**
+     * @var string
+     */
+    protected $returnType = BlockedDomain::class;
+
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
-    protected $updatedField = null;
+
+    protected $updatedField;
 
     /**
      * Retrieves instance or podcast domain blocks depending on whether or not $podcastId param is set.
@@ -47,7 +70,7 @@ class BlockedDomainModel extends Model
             config('ActivityPub')->cachePrefix .
             "domain#{$hashedDomain}_isBlocked";
         if (!($found = cache($cacheName))) {
-            $found = $this->find($domain) ? true : false;
+            $found = (bool) $this->find($domain);
 
             cache()->save($cacheName, $found, DECADE);
         }
@@ -83,6 +106,9 @@ class BlockedDomainModel extends Model
         return $result;
     }
 
+    /**
+     * @return bool|BaseResult
+     */
     public function unblockDomain($name)
     {
         $hashedDomain = md5($name);
diff --git a/app/Libraries/ActivityPub/Models/FavouriteModel.php b/app/Libraries/ActivityPub/Models/FavouriteModel.php
index 4aacdc9a2d..8dbb292a86 100644
--- a/app/Libraries/ActivityPub/Models/FavouriteModel.php
+++ b/app/Libraries/ActivityPub/Models/FavouriteModel.php
@@ -8,6 +8,9 @@
 
 namespace ActivityPub\Models;
 
+use ActivityPub\Entities\Actor;
+use ActivityPub\Entities\Note;
+use ActivityPub\Entities\Favourite;
 use ActivityPub\Activities\LikeActivity;
 use ActivityPub\Activities\UndoActivity;
 use CodeIgniter\Events\Events;
@@ -15,25 +18,38 @@ use Michalsn\Uuid\UuidModel;
 
 class FavouriteModel extends UuidModel
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_favourites';
+
+    /**
+     * @var string[]
+     */
     protected $uuidFields = ['note_id'];
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['actor_id', 'note_id'];
 
-    protected $returnType = \ActivityPub\Entities\Favourite::class;
-
-    protected $useTimestamps = true;
-    protected $updatedField = null;
+    /**
+     * @var string
+     */
+    protected $returnType = Favourite::class;
 
     /**
-     *
-     * @param \ActivityPub\Entities\Actor $actor
-     * @param \ActivityPub\Entities\Note $note
-     * @param bool $registerActivity
-     * @return void
+     * @var bool
      */
-    public function addFavourite($actor, $note, $registerActivity = true)
-    {
+    protected $useTimestamps = true;
+
+    protected $updatedField;
+
+    public function addFavourite(
+        Actor $actor,
+        Note $note,
+        bool $registerActivity = true
+    ): void {
         $this->db->transStart();
 
         $this->insert([
@@ -53,7 +69,7 @@ class FavouriteModel extends UuidModel
         $prefix = config('ActivityPub')->cachePrefix;
         $hashedNoteUri = md5($note->uri);
         cache()->delete($prefix . "note#{$note->id}");
-        cache()->delete($prefix . "note@{$hashedNoteUri}");
+        cache()->delete($prefix . "note-{$hashedNoteUri}");
         cache()->delete($prefix . "actor#{$actor->id}_published_notes");
 
         if ($note->in_reply_to_id) {
@@ -92,8 +108,11 @@ class FavouriteModel extends UuidModel
         $this->db->transComplete();
     }
 
-    public function removeFavourite($actor, $note, $registerActivity = true)
-    {
+    public function removeFavourite(
+        $actor,
+        $note,
+        $registerActivity = true
+    ): void {
         $this->db->transStart();
 
         model('NoteModel')
@@ -108,7 +127,7 @@ class FavouriteModel extends UuidModel
         $prefix = config('ActivityPub')->cachePrefix;
         $hashedNoteUri = md5($note->uri);
         cache()->delete($prefix . "note#{$note->id}");
-        cache()->delete($prefix . "note@{$hashedNoteUri}");
+        cache()->delete($prefix . "note-{$hashedNoteUri}");
         cache()->delete($prefix . "actor#{$actor->id}_published_notes");
 
         if ($note->in_reply_to_id) {
@@ -182,12 +201,8 @@ class FavouriteModel extends UuidModel
 
     /**
      * Adds or removes favourite from database and increments count
-     *
-     * @param \ActivityPub\Entities\Actor $actor
-     * @param \ActivityPub\Entities\Note $note
-     * @return void
      */
-    public function toggleFavourite($actor, $note)
+    public function toggleFavourite(Actor $actor, Note $note): void
     {
         if (
             $this->where([
diff --git a/app/Libraries/ActivityPub/Models/FollowModel.php b/app/Libraries/ActivityPub/Models/FollowModel.php
index ced37815ed..4bd0031bf0 100644
--- a/app/Libraries/ActivityPub/Models/FollowModel.php
+++ b/app/Libraries/ActivityPub/Models/FollowModel.php
@@ -8,6 +8,9 @@
 
 namespace ActivityPub\Models;
 
+use ActivityPub\Entities\Actor;
+use ActivityPub\Entities\Follow;
+use Exception;
 use ActivityPub\Activities\FollowActivity;
 use ActivityPub\Activities\UndoActivity;
 use CodeIgniter\Database\Exceptions\DatabaseException;
@@ -17,24 +20,38 @@ use InvalidArgumentException;
 
 class FollowModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_follows';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['actor_id', 'target_actor_id'];
 
-    protected $returnType = \ActivityPub\Entities\Follow::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Follow::class;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
-    protected $updatedField = null;
+
+    protected $updatedField;
 
     /**
-     * @param \ActivityPub\Entities\Actor $actor Actor that is following
-     * @param \ActivityPub\Entities\Actor $targetActor Actor that is being followed
-     * @param bool $registerActivity
-     * @return void
+     * @param Actor $actor Actor that is following
+     * @param Actor $targetActor Actor that is being followed
      * @throws DatabaseException
      */
-    public function addFollower($actor, $targetActor, $registerActivity = true)
-    {
+    public function addFollower(
+        Actor $actor,
+        Actor $targetActor,
+        bool $registerActivity = true
+    ): void {
         try {
             $this->db->transStart();
 
@@ -86,23 +103,22 @@ class FollowModel extends Model
             }
 
             $this->db->transComplete();
-        } catch (\Exception $e) {
+        } catch (Exception $exception) {
             // follow already exists, do nothing
         }
     }
 
     /**
-     * @param \ActivityPub\Entities\Actor $actor Actor that is unfollowing
-     * @param \ActivityPub\Entities\Actor $targetActor Actor that is being unfollowed
-     * @return void
+     * @param Actor $actor Actor that is unfollowing
+     * @param Actor $targetActor Actor that is being unfollowed
      * @throws InvalidArgumentException
      * @throws DatabaseException
      */
     public function removeFollower(
-        $actor,
-        $targetActor,
+        Actor $actor,
+        Actor $targetActor,
         $registerActivity = true
-    ) {
+    ): void {
         $this->db->transStart();
 
         $this->where([
diff --git a/app/Libraries/ActivityPub/Models/NoteModel.php b/app/Libraries/ActivityPub/Models/NoteModel.php
index ed1600f3d1..fe33f0df8b 100644
--- a/app/Libraries/ActivityPub/Models/NoteModel.php
+++ b/app/Libraries/ActivityPub/Models/NoteModel.php
@@ -8,6 +8,9 @@
 
 namespace ActivityPub\Models;
 
+use ActivityPub\Entities\Actor;
+use CodeIgniter\Database\Query;
+use Exception;
 use ActivityPub\Entities\Note;
 use ActivityPub\Activities\AnnounceActivity;
 use ActivityPub\Activities\CreateActivity;
@@ -21,11 +24,23 @@ use Michalsn\Uuid\UuidModel;
 
 class NoteModel extends UuidModel
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_notes';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $uuidFields = ['id', 'in_reply_to_id', 'reblog_of_id'];
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'uri',
@@ -40,17 +55,34 @@ class NoteModel extends UuidModel
         'published_at',
     ];
 
-    protected $returnType = \ActivityPub\Entities\Note::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Note::class;
+
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
-    protected $updatedField = null;
 
+    protected $updatedField;
+
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'actor_id' => 'required',
         'message_html' => 'required_without[reblog_of_id]|max_length[500]',
     ];
 
+    /**
+     * @var string[]
+     */
     protected $beforeInsert = ['setNoteId'];
 
     public function getNoteById($noteId)
@@ -69,7 +101,7 @@ class NoteModel extends UuidModel
     {
         $hashedNoteUri = md5($noteUri);
         $cacheName =
-            config('ActivityPub')->cachePrefix . "note@{$hashedNoteUri}";
+            config('ActivityPub')->cachePrefix . "note-{$hashedNoteUri}";
         if (!($found = cache($cacheName))) {
             $found = $this->where('uri', $noteUri)->first();
 
@@ -82,9 +114,9 @@ class NoteModel extends UuidModel
     /**
      * Retrieves all published notes for a given actor ordered by publication date
      *
-     * @return \ActivityPub\Entities\Note[]
+     * @return Note[]
      */
-    public function getActorPublishedNotes($actorId)
+    public function getActorPublishedNotes($actorId): array
     {
         $cacheName =
             config('ActivityPub')->cachePrefix .
@@ -108,12 +140,12 @@ class NoteModel extends UuidModel
      * Retrieves all published replies for a given note.
      * By default, it does not get replies from blocked actors.
      *
-     * @param mixed $noteId
-     * @param boolean $withBlocked false by default
-     * @return array
+     * @return Note[]
      */
-    public function getNoteReplies($noteId, $withBlocked = false)
-    {
+    public function getNoteReplies(
+        string $noteId,
+        bool $withBlocked = false
+    ): array {
         $cacheName =
             config('ActivityPub')->cachePrefix .
             "note#{$noteId}_replies" .
@@ -167,6 +199,9 @@ class NoteModel extends UuidModel
         return $found;
     }
 
+    /**
+     * @return bool|Query
+     */
     public function addPreviewCard($noteId, $previewCardId)
     {
         return $this->db->table('activitypub_notes_preview_cards')->insert([
@@ -178,15 +213,12 @@ class NoteModel extends UuidModel
     /**
      * Adds note in database along preview card if relevant
      *
-     * @param \ActivityPub\Entities\Note $note
-     * @param boolean $registerActivity
-     * @param boolean $createPreviewCard
      * @return string|false returns the new note id if success or false otherwise
      */
     public function addNote(
-        $note,
-        $createPreviewCard = true,
-        $registerActivity = true
+        Note $note,
+        bool $createPreviewCard = true,
+        bool $registerActivity = true
     ) {
         helper('activitypub');
 
@@ -207,14 +239,12 @@ class NoteModel extends UuidModel
                 !empty($messageUrls) &&
                 ($previewCard = get_or_create_preview_card_from_url(
                     new URI($messageUrls[0]),
-                ))
+                )) &&
+                !$this->addPreviewCard($newNoteId, $previewCard->id)
             ) {
-                if (!$this->addPreviewCard($newNoteId, $previewCard->id)) {
-                    $this->db->transRollback();
-
-                    // problem when linking note to preview card
-                    return false;
-                }
+                $this->db->transRollback();
+                // problem when linking note to preview card
+                return false;
             }
         }
 
@@ -270,7 +300,7 @@ class NoteModel extends UuidModel
         return $newNoteId;
     }
 
-    public function editNote($updatedNote)
+    public function editNote($updatedNote): bool
     {
         $this->db->transStart();
 
@@ -290,7 +320,7 @@ class NoteModel extends UuidModel
             DATE_W3C,
         );
         model('ActivityModel')->update($scheduledActivity->id, [
-            'payload' => json_encode($newPayload),
+            'payload' => json_encode($newPayload, JSON_THROW_ON_ERROR),
             'scheduled_at' => $updatedNote->published_at,
         ]);
 
@@ -301,7 +331,7 @@ class NoteModel extends UuidModel
         $prefix = config('ActivityPub')->cachePrefix;
         $hashedNoteUri = md5($updatedNote->uri);
         cache()->delete($prefix . "note#{$updatedNote->id}");
-        cache()->delete($prefix . "note@{$hashedNoteUri}");
+        cache()->delete($prefix . "note-{$hashedNoteUri}");
 
         $this->db->transComplete();
 
@@ -311,10 +341,9 @@ class NoteModel extends UuidModel
     /**
      * Removes a note from the database and decrements meta data
      *
-     * @param \ActivityPub\Entities\Note $note
-     * @return mixed
+     * @return BaseResult|bool
      */
-    public function removeNote($note, $registerActivity = true)
+    public function removeNote(Note $note, bool $registerActivity = true)
     {
         $this->db->transStart();
 
@@ -339,7 +368,7 @@ class NoteModel extends UuidModel
 
             $replyToNote = $note->reply_to_note;
             cache()->delete($cachePrefix . "note#{$replyToNote->id}");
-            cache()->delete($cachePrefix . "note@{$replyToNote->uri}");
+            cache()->delete($cachePrefix . "note-{$replyToNote->uri}");
             cache()->delete($cachePrefix . "note#{$replyToNote->id}_replies");
             cache()->delete(
                 $cachePrefix . "note#{$replyToNote->id}_replies_withBlocked",
@@ -358,19 +387,18 @@ class NoteModel extends UuidModel
             $this->removeNote($reply);
         }
 
-        if ($note->preview_card) {
-            // check that preview card in no longer used elsewhere before deleting it
-            if (
-                $this->db
-                    ->table('activitypub_notes_preview_cards')
-                    ->where('preview_card_id', $note->preview_card->id)
-                    ->countAll() <= 1
-            ) {
-                model('PreviewCardModel')->deletePreviewCard(
-                    $note->preview_card->id,
-                    $note->preview_card->url,
-                );
-            }
+        // check that preview card in no longer used elsewhere before deleting it
+        if (
+            $note->preview_card &&
+            $this->db
+                ->table('activitypub_notes_preview_cards')
+                ->where('preview_card_id', $note->preview_card->id)
+                ->countAll() <= 1
+        ) {
+            model('PreviewCardModel')->deletePreviewCard(
+                $note->preview_card->id,
+                $note->preview_card->url,
+            );
         }
 
         Events::trigger('on_note_remove', $note);
@@ -408,7 +436,7 @@ class NoteModel extends UuidModel
         // clear note + replies / reblogs + actor  and its published notes
         $hashedNoteUri = md5($note->uri);
         cache()->delete($cachePrefix . "note#{$note->id}");
-        cache()->delete($cachePrefix . "note@{$hashedNoteUri}");
+        cache()->delete($cachePrefix . "note-{$hashedNoteUri}");
         cache()->delete($cachePrefix . "note#{$note->id}_replies");
         cache()->delete($cachePrefix . "note#{$note->id}_replies_withBlocked");
         cache()->delete($cachePrefix . "note#{$note->id}_reblogs");
@@ -421,13 +449,16 @@ class NoteModel extends UuidModel
         return $result;
     }
 
+    /**
+     * @return string|bool
+     */
     public function addReply(
         $reply,
         $createPreviewCard = true,
         $registerActivity = true
     ) {
         if (!$reply->in_reply_to_id) {
-            throw new \Exception('Passed note is not a reply!');
+            throw new Exception('Passed note is not a reply!');
         }
 
         $this->db->transStart();
@@ -444,7 +475,7 @@ class NoteModel extends UuidModel
         $prefix = config('ActivityPub')->cachePrefix;
         $hashedNoteUri = md5($reply->reply_to_note->uri);
         cache()->delete($prefix . "note#{$reply->in_reply_to_id}");
-        cache()->delete($prefix . "note@{$hashedNoteUri}");
+        cache()->delete($prefix . "note-{$hashedNoteUri}");
         cache()->delete($prefix . "note#{$reply->in_reply_to_id}_replies");
         cache()->delete(
             $prefix . "note#{$reply->in_reply_to_id}_replies_withBlocked",
@@ -458,12 +489,9 @@ class NoteModel extends UuidModel
     }
 
     /**
-     *
-     * @param \ActivityPub\Entities\Actor $actor
-     * @param \ActivityPub\Entities\Note $note
      * @return ActivityPub\Models\BaseResult|int|string|false
      */
-    public function reblog($actor, $note, $registerActivity = true)
+    public function reblog(Actor $actor, Note $note, $registerActivity = true)
     {
         $this->db->transStart();
 
@@ -490,7 +518,7 @@ class NoteModel extends UuidModel
 
         $hashedNoteUri = md5($note->uri);
         cache()->delete($prefix . "note#{$note->id}");
-        cache()->delete($prefix . "note@{$hashedNoteUri}");
+        cache()->delete($prefix . "note-{$hashedNoteUri}");
         cache()->delete($prefix . "note#{$note->id}_reblogs");
 
         Events::trigger('on_note_reblog', $actor, $note);
@@ -526,10 +554,9 @@ class NoteModel extends UuidModel
     }
 
     /**
-     * @param \ActivityPub\Entities\Note $reblogNote
-     * @return mixed
+     * @return BaseResult|bool
      */
-    public function undoReblog($reblogNote, $registerActivity = true)
+    public function undoReblog(Note $reblogNote, bool $registerActivity = true)
     {
         $this->db->transStart();
 
@@ -553,9 +580,9 @@ class NoteModel extends UuidModel
         $hashedReblogNoteUri = md5($reblogNote->uri);
         $hashedNoteUri = md5($reblogNote->reblog_of_note->uri);
         cache()->delete($cachePrefix . "note#{$reblogNote->id}");
-        cache()->delete($cachePrefix . "note@{$hashedReblogNoteUri}");
+        cache()->delete($cachePrefix . "note-{$hashedReblogNoteUri}");
         cache()->delete($cachePrefix . "note#{$reblogNote->reblog_of_id}");
-        cache()->delete($cachePrefix . "note@{$hashedNoteUri}");
+        cache()->delete($cachePrefix . "note-{$hashedNoteUri}");
 
         Events::trigger('on_note_undo_reblog', $reblogNote);
 
@@ -621,7 +648,7 @@ class NoteModel extends UuidModel
         return $result;
     }
 
-    public function toggleReblog($actor, $note)
+    public function toggleReblog($actor, $note): void
     {
         if (
             !($reblogNote = $this->where([
diff --git a/app/Libraries/ActivityPub/Models/PreviewCardModel.php b/app/Libraries/ActivityPub/Models/PreviewCardModel.php
index f7dc02aea6..5580e7bb94 100644
--- a/app/Libraries/ActivityPub/Models/PreviewCardModel.php
+++ b/app/Libraries/ActivityPub/Models/PreviewCardModel.php
@@ -8,12 +8,20 @@
 
 namespace ActivityPub\Models;
 
+use CodeIgniter\Database\BaseResult;
+use ActivityPub\Entities\PreviewCard;
 use CodeIgniter\Model;
 
 class PreviewCardModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'activitypub_preview_cards';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'url',
@@ -28,9 +36,19 @@ class PreviewCardModel extends Model
         'html',
     ];
 
-    protected $returnType = \ActivityPub\Entities\PreviewCard::class;
+    /**
+     * @var string
+     */
+    protected $returnType = PreviewCard::class;
+
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
     public function getPreviewCardFromUrl($url)
@@ -38,7 +56,7 @@ class PreviewCardModel extends Model
         $hashedPreviewCardUrl = md5($url);
         $cacheName =
             config('ActivityPub')->cachePrefix .
-            "preview_card@{$hashedPreviewCardUrl}";
+            "preview_card-{$hashedPreviewCardUrl}";
         if (!($found = cache($cacheName))) {
             $found = $this->where('url', $url)->first();
             cache()->save($cacheName, $found, DECADE);
@@ -71,12 +89,15 @@ class PreviewCardModel extends Model
         return $found;
     }
 
+    /**
+     * @return bool|BaseResult
+     */
     public function deletePreviewCard($id, $url)
     {
         $hashedPreviewCardUrl = md5($url);
         cache()->delete(
             config('ActivityPub')->cachePrefix .
-                "preview_card@{$hashedPreviewCardUrl}",
+                "preview_card-{$hashedPreviewCardUrl}",
         );
 
         return $this->delete($id);
diff --git a/app/Libraries/ActivityPub/Objects/ActorObject.php b/app/Libraries/ActivityPub/Objects/ActorObject.php
index 4fa0b8062f..819d2991f0 100644
--- a/app/Libraries/ActivityPub/Objects/ActorObject.php
+++ b/app/Libraries/ActivityPub/Objects/ActorObject.php
@@ -8,6 +8,7 @@
 
 namespace ActivityPub\Objects;
 
+use ActivityPub\Entities\Actor;
 use ActivityPub\Core\ObjectType;
 
 class ActorObject extends ObjectType
@@ -68,17 +69,14 @@ class ActorObject extends ObjectType
     /**
      * @var array
      */
-    protected $icon;
+    protected $icon = [];
 
     /**
      * @var object
      */
     protected $publicKey;
 
-    /**
-     * @param \ActivityPub\Entities\Actor $podcast
-     */
-    public function __construct($actor)
+    public function __construct(Actor $actor)
     {
         $this->id = $actor->uri;
 
diff --git a/app/Libraries/ActivityPub/Objects/NoteObject.php b/app/Libraries/ActivityPub/Objects/NoteObject.php
index 92a83c53fb..2c2f6c3645 100644
--- a/app/Libraries/ActivityPub/Objects/NoteObject.php
+++ b/app/Libraries/ActivityPub/Objects/NoteObject.php
@@ -13,6 +13,7 @@
 
 namespace ActivityPub\Objects;
 
+use ActivityPub\Entities\Note;
 use ActivityPub\Core\ObjectType;
 
 class NoteObject extends ObjectType
@@ -35,10 +36,10 @@ class NoteObject extends ObjectType
     /**
      * @var array
      */
-    protected $replies;
+    protected $replies = [];
 
     /**
-     * @param \ActivityPub\Entities\Note $note
+     * @param Note $note
      */
     public function __construct($note)
     {
diff --git a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php
index faabb78b1e..639bbc97f3 100644
--- a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php
+++ b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php
@@ -11,10 +11,15 @@
 
 namespace ActivityPub\Objects;
 
+use ActivityPub\Core\Activity;
+use CodeIgniter\Pager\Pager;
 use ActivityPub\Core\ObjectType;
 
 class OrderedCollectionObject extends ObjectType
 {
+    /**
+     * @var string
+     */
     protected $type = 'OrderedCollection';
 
     /**
@@ -43,18 +48,19 @@ class OrderedCollectionObject extends ObjectType
     protected $orderedItems;
 
     /**
-     * @param \ActivityPub\Libraries\ActivityPub\Activity[] $orderedItems
-     * @param \CodeIgniter\Pager\Pager $pager
+     * @param array $orderedItems
      */
-    public function __construct($orderedItems, $pager = null)
-    {
+    public function __construct(
+        ?array $orderedItems = null,
+        ?Pager $pager = null
+    ) {
         $this->id = current_url();
 
-        if ($pager) {
+        if ($pager !== null) {
             $totalItems = $pager->getTotal();
             $this->totalItems = $totalItems;
 
-            if ($totalItems) {
+            if ($totalItems !== 0) {
                 $this->first = $pager->getPageURI($pager->getFirstPage());
                 $this->current = $pager->getPageURI();
                 $this->last = $pager->getPageURI($pager->getLastPage());
diff --git a/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php b/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php
index 98eba8f257..a5a5dde4e9 100644
--- a/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php
+++ b/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php
@@ -11,6 +11,7 @@
 
 namespace ActivityPub\Objects;
 
+use CodeIgniter\Pager\Pager;
 class OrderedCollectionPage extends OrderedCollectionObject
 {
     /**
@@ -33,18 +34,18 @@ class OrderedCollectionPage extends OrderedCollectionObject
      */
     protected $next;
 
-    /**
-     * @param \CodeIgniter\Pager\Pager $pager
-     * @param \ActivityPub\Libraries\ActivityPub\Activity[] $orderedItems
-     */
-    public function __construct($pager, $orderedItems)
+    public function __construct(Pager $pager, ?array $orderedItems = null)
     {
         parent::__construct($orderedItems, $pager);
 
         $isFirstPage = $pager->getCurrentPage() === $pager->getFirstPage();
         $isLastPage = $pager->getCurrentPage() === $pager->getLastPage();
-        $isFirstPage && ($this->first = null);
-        $isLastPage && ($this->last = null);
+        if ($isFirstPage) {
+            $this->first = null;
+        }
+        if ($isLastPage) {
+            $this->last = null;
+        }
 
         $this->id = $pager->getPageURI($pager->getCurrentPage());
         $this->partOf = $pager->getPageURI();
diff --git a/app/Libraries/ActivityPub/WebFinger.php b/app/Libraries/ActivityPub/WebFinger.php
index 58f6ce4b88..c273a0758e 100644
--- a/app/Libraries/ActivityPub/WebFinger.php
+++ b/app/Libraries/ActivityPub/WebFinger.php
@@ -12,6 +12,9 @@ use Exception;
 
 class WebFinger
 {
+    /**
+     * @var string
+     */
     const RESOURCE_PATTERN = '/^acct:(?P<username>([\w_]+))@(?P<domain>([\w\-\.]+[\w]+)(:[\d]+)?)$/x';
 
     /**
@@ -19,6 +22,11 @@ class WebFinger
      */
     protected $username;
 
+    /**
+     * @var string
+     */
+    protected $domain;
+
     /**
      * @var string
      */
@@ -37,12 +45,12 @@ class WebFinger
     /**
      * @var array
      */
-    protected $aliases;
+    protected $aliases = [];
 
     /**
-     * @var string
+     * @var array
      */
-    protected $links;
+    protected $links = [];
 
     /**
      * @param string $resource
@@ -94,11 +102,23 @@ class WebFinger
         ];
     }
 
+    /**
+     * Get WebFinger response as an array
+     *
+     * @return array<string, array|string>
+     */
+    public function toArray(): array
+    {
+        return [
+            'subject' => $this->subject,
+            'aliases' => $this->aliases,
+            'links' => $this->links,
+        ];
+    }
     /**
      * Split resource into its parts (username, domain)
      *
-     * @param string $resource
-     * @return bool|array
+     * @return bool|mixed
      */
     private function splitResource(string $resource)
     {
@@ -109,18 +129,4 @@ class WebFinger
 
         return $matches;
     }
-
-    /**
-     * Get WebFinger response as an array
-     *
-     * @return array
-     */
-    public function toArray()
-    {
-        return [
-            'subject' => $this->subject,
-            'aliases' => $this->aliases,
-            'links' => $this->links,
-        ];
-    }
 }
diff --git a/app/Libraries/Analytics/AnalyticsTrait.php b/app/Libraries/Analytics/AnalyticsTrait.php
index 2b0a69e70c..4f9a374c5b 100644
--- a/app/Libraries/Analytics/AnalyticsTrait.php
+++ b/app/Libraries/Analytics/AnalyticsTrait.php
@@ -8,14 +8,11 @@
 
 namespace Analytics;
 
+use Config\Services;
+use Config\Database;
 trait AnalyticsTrait
 {
-    /**
-     *
-     * @param integer $podcastId
-     * @return void
-     */
-    protected function registerPodcastWebpageHit($podcastId)
+    protected function registerPodcastWebpageHit(int $podcastId): void
     {
         helper('analytics');
 
@@ -24,11 +21,11 @@ trait AnalyticsTrait
         set_user_session_referer();
         set_user_session_entry_page();
 
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         if (!$session->get('denyListIp')) {
-            $db = \Config\Database::connect();
+            $db = Database::connect();
 
             $referer = $session->get('referer');
             $domain = empty(parse_url($referer, PHP_URL_HOST))
@@ -38,7 +35,7 @@ trait AnalyticsTrait
             $keywords = empty($queries['q']) ? null : $queries['q'];
 
             $procedureName = $db->prefixTable('analytics_website');
-            $db->query("call $procedureName(?,?,?,?,?,?)", [
+            $db->query("call {$procedureName}(?,?,?,?,?,?)", [
                 $podcastId,
                 $session->get('browser'),
                 $session->get('entryPage'),
diff --git a/app/Libraries/Analytics/Config/Analytics.php b/app/Libraries/Analytics/Config/Analytics.php
index 64a85b5d25..28cf7379d1 100644
--- a/app/Libraries/Analytics/Config/Analytics.php
+++ b/app/Libraries/Analytics/Config/Analytics.php
@@ -18,6 +18,7 @@ class Analytics extends BaseConfig
      * --------------------------------------------------------------------
      * Route filters options
      * --------------------------------------------------------------------
+     * @var array<string, string>
      */
     public $routeFilters = [
         'analytics-full-data' => '',
@@ -27,11 +28,8 @@ class Analytics extends BaseConfig
 
     /**
      * get the full audio file url
-     *
-     * @param string $filename
-     * @return string
      */
-    public function getAudioFileUrl(string $audioFilePath)
+    public function getAudioFileUrl(string $audioFilePath): string
     {
         return base_url($audioFilePath);
     }
diff --git a/app/Libraries/Analytics/Config/Routes.php b/app/Libraries/Analytics/Config/Routes.php
index 62de878c13..15b11a6925 100644
--- a/app/Libraries/Analytics/Config/Routes.php
+++ b/app/Libraries/Analytics/Config/Routes.php
@@ -21,10 +21,10 @@ $routes->addPlaceholder(
 
 $routes->group('', ['namespace' => 'Analytics\Controllers'], function (
     $routes
-) {
+): void {
     $routes->group(config('Analytics')->gateway . '/(:num)/(:class)', function (
         $routes
-    ) {
+    ): void {
         $routes->get('/', 'AnalyticsController::getData/$1/$2', [
             'as' => 'analytics-full-data',
             'filter' => config('Analytics')->routeFilters[
diff --git a/app/Libraries/Analytics/Controllers/AnalyticsController.php b/app/Libraries/Analytics/Controllers/AnalyticsController.php
index d80f03d5cf..139892b332 100644
--- a/app/Libraries/Analytics/Controllers/AnalyticsController.php
+++ b/app/Libraries/Analytics/Controllers/AnalyticsController.php
@@ -8,6 +8,8 @@
 
 namespace Analytics\Controllers;
 
+use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Exceptions\PageNotFoundException;
 use CodeIgniter\Controller;
 
 class AnalyticsController extends Controller
@@ -25,7 +27,7 @@ class AnalyticsController extends Controller
     public function _remap($method, ...$params)
     {
         if (!isset($params[1])) {
-            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+            throw PageNotFoundException::forPageNotFound();
         }
 
         $this->className = model('Analytics' . $params[1] . 'Model');
@@ -37,7 +39,7 @@ class AnalyticsController extends Controller
         );
     }
 
-    public function getData($podcastId, $episodeId)
+    public function getData($podcastId, $episodeId): ResponseInterface
     {
         $analytics_model = new $this->className();
         $methodName = $this->methodName;
@@ -45,10 +47,10 @@ class AnalyticsController extends Controller
             return $this->response->setJSON(
                 $analytics_model->$methodName($podcastId, $episodeId),
             );
-        } else {
-            return $this->response->setJSON(
-                $analytics_model->$methodName($podcastId),
-            );
         }
+
+        return $this->response->setJSON(
+            $analytics_model->$methodName($podcastId),
+        );
     }
 }
diff --git a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php
index b9a2cb4376..f2fa71f758 100644
--- a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php
+++ b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php
@@ -8,6 +8,11 @@
 
 namespace Analytics\Controllers;
 
+use Analytics\Config\Analytics;
+use CodeIgniter\HTTP\RequestInterface;
+use CodeIgniter\HTTP\ResponseInterface;
+use Psr\Log\LoggerInterface;
+use Config\Services;
 use CodeIgniter\Controller;
 
 class EpisodeAnalyticsController extends Controller
@@ -22,17 +27,17 @@ class EpisodeAnalyticsController extends Controller
     protected $helpers = ['analytics'];
 
     /**
-     * @var \Analytics\Config\Analytics
+     * @var Analytics
      */
     protected $config;
     /**
      * Constructor.
      */
     public function initController(
-        \CodeIgniter\HTTP\RequestInterface $request,
-        \CodeIgniter\HTTP\ResponseInterface $response,
-        \Psr\Log\LoggerInterface $logger
-    ) {
+        RequestInterface $request,
+        ResponseInterface $response,
+        LoggerInterface $logger
+    ): void {
         // Do Not Edit This Line
         parent::initController($request, $response, $logger);
 
@@ -52,8 +57,9 @@ class EpisodeAnalyticsController extends Controller
     // Add one hit to this episode:
     public function hit($base64EpisodeData, ...$audioFilePath)
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
+
         $serviceName = '';
         if (isset($_GET['_from'])) {
             $serviceName = $_GET['_from'];
diff --git a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php
index c26648189e..7d5549a026 100644
--- a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php
+++ b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php
@@ -8,11 +8,12 @@
 
 namespace Analytics\Controllers;
 
+use CodeIgniter\HTTP\ResponseInterface;
 use CodeIgniter\Controller;
 
 class UnknownUserAgentsController extends Controller
 {
-    public function index($lastKnownId = 0)
+    public function index($lastKnownId = 0): ResponseInterface
     {
         $model = model('UnknownUserAgentsModel');
 
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php
index e89b1d19c9..7db412ae76 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcasts extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -54,7 +54,7 @@ class AddAnalyticsPodcasts extends Migration
         $this->forge->createTable('analytics_podcasts');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php
index 72d9b861c0..ebd78f3f3a 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsByEpisode extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -50,7 +50,7 @@ class AddAnalyticsPodcastsByEpisode extends Migration
         $this->forge->createTable('analytics_podcasts_by_episode');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts_by_episode');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php
index 4e898f4134..31f2ed7250 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsByHour extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -45,7 +45,7 @@ class AddAnalyticsPodcastsByHour extends Migration
         $this->forge->createTable('analytics_podcasts_by_hour');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts_by_hour');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php
index 424ede824a..a2429dd5dc 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsByPlayer extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -70,7 +70,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration
         $this->forge->createTable('analytics_podcasts_by_player');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts_by_player');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php
index d2a14f2279..4fe827ffe2 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsByCountry extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -46,7 +46,7 @@ class AddAnalyticsPodcastsByCountry extends Migration
         $this->forge->createTable('analytics_podcasts_by_country');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts_by_country');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php
index ee55120deb..51a604865b 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsByRegion extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -64,7 +64,7 @@ class AddAnalyticsPodcastsByRegion extends Migration
         $this->forge->createTable('analytics_podcasts_by_region');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_podcasts_by_region');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php
index dbfd70e659..3bd16c3ab2 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddPodcastsPlatforms extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -51,7 +51,7 @@ class AddPodcastsPlatforms extends Migration
         $this->forge->createTable('podcasts_platforms');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('podcasts_platforms');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php
index 5a542c4978..80cf8dcb6e 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsWebsiteByBrowser extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -46,7 +46,7 @@ class AddAnalyticsWebsiteByBrowser extends Migration
         $this->forge->createTable('analytics_website_by_browser');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_website_by_browser');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php
index 4571142946..9bbfd9ede7 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsWebsiteByReferer extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -55,7 +55,7 @@ class AddAnalyticsWebsiteByReferer extends Migration
         $this->forge->createTable('analytics_website_by_referer');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_website_by_referer');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php
index 8e331a6e17..d422dad307 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsWebsiteByEntryPage extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'podcast_id' => [
@@ -45,7 +45,7 @@ class AddAnalyticsWebsiteByEntryPage extends Migration
         $this->forge->createTable('analytics_website_by_entry_page');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_website_by_entry_page');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php
index 2ff7fcea7e..60db815966 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsUnknownUseragents extends Migration
 {
-    public function up()
+    public function up(): void
     {
         $this->forge->addField([
             'id' => [
@@ -46,7 +46,7 @@ class AddAnalyticsUnknownUseragents extends Migration
         $this->forge->createTable('analytics_unknown_useragents');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('analytics_unknown_useragents');
     }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php
index ca579b3a5a..7e2052d48b 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php
@@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsPodcastsProcedure extends Migration
 {
-    public function up()
+    public function up(): void
     {
         // Creates Procedure for data insertion
         // Example: CALL analytics_podcasts(1, 2, 'FR', 'IDF', 48.853, 2.349, PodcastAddict, 'phone', 'android', 0, 1);
@@ -77,7 +77,7 @@ class AddAnalyticsPodcastsProcedure extends Migration
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $prefix = $this->db->getPrefix();
         $this->db->query(
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php
index 5f6a65a934..67bb58133d 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php
@@ -15,26 +15,26 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsUnknownUseragentsProcedure extends Migration
 {
-    public function up()
+    public function up(): void
     {
         // Creates Procedure for data insertion
         // Example: CALL analytics_unknown_useragents('Podcasts/1430.46 CFNetwork/1125.2 Darwin/19.4.0');
         $procedureName = $this->db->prefixTable('analytics_unknown_useragents');
         $createQuery = <<<EOD
-        CREATE PROCEDURE `$procedureName` (IN `p_useragent` VARCHAR(191) CHARSET utf8mb4)  MODIFIES SQL DATA
+        CREATE PROCEDURE `{$procedureName}` (IN `p_useragent` VARCHAR(191) CHARSET utf8mb4)  MODIFIES SQL DATA
         DETERMINISTIC
         SQL SECURITY INVOKER
-        COMMENT 'Add an unknown useragent to table $procedureName.'
-        INSERT INTO `$procedureName`(`useragent`)
+        COMMENT 'Add an unknown useragent to table {$procedureName}.'
+        INSERT INTO `{$procedureName}`(`useragent`)
         VALUES (p_useragent)
         ON DUPLICATE KEY UPDATE `hits`=`hits`+1
         EOD;
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $procedureName = $this->db->prefixTable('analytics_unknown_useragents');
-        $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`");
+        $this->db->query("DROP PROCEDURE IF EXISTS `{$procedureName}`");
     }
 }
diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php
index c4e5ac8ce8..3817fcfc12 100644
--- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php
+++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php
@@ -15,13 +15,13 @@ use CodeIgniter\Database\Migration;
 
 class AddAnalyticsWebsiteProcedure extends Migration
 {
-    public function up()
+    public function up(): void
     {
         // Creates Procedure for data insertion
         // Example: CALL analytics_website(1,'FR','Firefox');
         $procedureName = $this->db->prefixTable('analytics_website');
         $createQuery = <<<EOD
-        CREATE PROCEDURE `$procedureName` (IN `p_podcast_id` INT UNSIGNED, IN `p_browser` VARCHAR(191) CHARSET utf8mb4, IN `p_entry_page` VARCHAR(512) CHARSET utf8mb4, IN `p_referer_url` VARCHAR(512) CHARSET utf8mb4, IN `p_domain` VARCHAR(128) CHARSET utf8mb4, IN `p_keywords` VARCHAR(384) CHARSET utf8mb4)  MODIFIES SQL DATA
+        CREATE PROCEDURE `{$procedureName}` (IN `p_podcast_id` INT UNSIGNED, IN `p_browser` VARCHAR(191) CHARSET utf8mb4, IN `p_entry_page` VARCHAR(512) CHARSET utf8mb4, IN `p_referer_url` VARCHAR(512) CHARSET utf8mb4, IN `p_domain` VARCHAR(128) CHARSET utf8mb4, IN `p_keywords` VARCHAR(384) CHARSET utf8mb4)  MODIFIES SQL DATA
         DETERMINISTIC
         SQL SECURITY INVOKER
         COMMENT 'Add one hit in website logs tables.'
@@ -43,9 +43,9 @@ class AddAnalyticsWebsiteProcedure extends Migration
         $this->db->query($createQuery);
     }
 
-    public function down()
+    public function down(): void
     {
         $procedureName = $this->db->prefixTable('analytics_website');
-        $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`");
+        $this->db->query("DROP PROCEDURE IF EXISTS `{$procedureName}`");
     }
 }
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php b/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php
index 1c05c27598..ef0df9a930 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcasts extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'date' => 'datetime',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php
index 0f03599a69..b02876dc6a 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php
@@ -10,7 +10,8 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByCountry extends Entity
 {
@@ -19,6 +20,9 @@ class AnalyticsPodcastsByCountry extends Entity
      */
     protected $labels;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'country_code' => 'string',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php
index d2e570da47..b512a9450f 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByEpisode extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'episode_id' => 'integer',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php
index 6dc44330e6..7794e5a3b1 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByHour extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'date' => 'datetime',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php
index 1bd636ee31..7bec2f4093 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByPlayer extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'app' => '?string',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php
index 7f62f57002..6afa194492 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByRegion extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'country_code' => 'string',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php
index d73eaeff53..e17fe5045b 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php
@@ -10,7 +10,9 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use Opawg\UserAgentsPhp\UserAgentsRSS;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsPodcastsByService extends Entity
 {
@@ -19,6 +21,9 @@ class AnalyticsPodcastsByService extends Entity
      */
     protected $labels;
 
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'app' => '?string',
@@ -31,8 +36,7 @@ class AnalyticsPodcastsByService extends Entity
 
     public function getLabels()
     {
-        return \Opawg\UserAgentsPhp\UserAgentsRSS::getName(
-            $this->attributes['labels'],
-        ) ?? $this->attributes['labels'];
+        return UserAgentsRSS::getName($this->attributes['labels']) ??
+            $this->attributes['labels'];
     }
 }
diff --git a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php b/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php
index b5a9dd3e76..4a843ab2b6 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php
@@ -10,10 +10,13 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsUnknownUseragents extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'useragent' => 'integer',
         'hits' => 'integer',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php
index d753e9e733..0ca6734c91 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsWebsiteByBrowser extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'browser' => 'string',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php
index 1e1c6d9896..f70fcbb373 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsWebsiteByEntryPage extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'entry_page_url' => 'string',
diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php
index 4fdf50b10b..980fe575f9 100644
--- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php
+++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php
@@ -10,10 +10,14 @@
 
 namespace Analytics\Entities;
 
-use CodeIgniter\Entity;
+use datetime;
+use CodeIgniter\Entity\Entity;
 
 class AnalyticsWebsiteByReferer extends Entity
 {
+    /**
+     * @var array<string, string>
+     */
     protected $casts = [
         'podcast_id' => 'integer',
         'referer_url' => 'string',
diff --git a/app/Libraries/Analytics/Helpers/analytics_helper.php b/app/Libraries/Analytics/Helpers/analytics_helper.php
index e18806a8af..d5d44f9471 100644
--- a/app/Libraries/Analytics/Helpers/analytics_helper.php
+++ b/app/Libraries/Analytics/Helpers/analytics_helper.php
@@ -1,5 +1,11 @@
 <?php
 
+use Config\Services;
+use Podlibre\Ipcat\IpDb;
+use GeoIp2\Database\Reader;
+use Opawg\UserAgentsPhp\UserAgents;
+use Config\Database;
+use WhichBrowser\Parser;
 /**
  * @copyright  2020 Podlibre
  * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
@@ -33,26 +39,17 @@ if (!function_exists('generate_episode_analytics_url')) {
      * Builds the episode analytics url that redirects to the audio file url
      * after analytics hit.
      *
-     * @param int $podcastId
-     * @param int $episodeId
-     * @param string $audioFilePath
-     * @param int $audioFileDuration
-     * @param int $audioFileSize
-     * @param int $audioFileHeaderSize
-     * @param \CodeIgniter\I18n\Time $publicationDate
-     *
-     * @return string
      * @throws RouterException
      */
     function generate_episode_analytics_url(
-        $podcastId,
-        $episodeId,
-        $audioFilePath,
-        $audioFileDuration,
-        $audioFileFilesize,
-        $audioFileHeaderSize,
-        $publicationDate
-    ) {
+        int $podcastId,
+        int $episodeId,
+        string $audioFilePath,
+        int $audioFileDuration,
+        int $audioFileSize,
+        int $audioFileHeaderSize,
+        \CodeIgniter\I18n\Time $publicationDate
+    ): string {
         return url_to(
             'episode-analytics-hit',
             base64_url_encode(
@@ -64,14 +61,14 @@ if (!function_exists('generate_episode_analytics_url')) {
                     // - if file is shorter than 60sec, then it's audio_file_size
                     // - if file is longer than 60 seconds then it's audio_file_header_size + 60 seconds
                     $audioFileDuration <= 60
-                        ? $audioFileFilesize
+                        ? $audioFileSize
                         : $audioFileHeaderSize +
                             floor(
-                                (($audioFileFilesize - $audioFileHeaderSize) /
+                                (($audioFileSize - $audioFileHeaderSize) /
                                     $audioFileDuration) *
                                     60,
                             ),
-                    $audioFileFilesize,
+                    $audioFileSize,
                     $audioFileDuration,
                     strtotime($publicationDate),
                 ),
@@ -85,15 +82,15 @@ if (!function_exists('set_user_session_deny_list_ip')) {
     /**
      * Set user country in session variable, for analytic purposes
      */
-    function set_user_session_deny_list_ip()
+    function set_user_session_deny_list_ip(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         if (!$session->has('denyListIp')) {
             $session->set(
                 'denyListIp',
-                \Podlibre\Ipcat\IpDb::find($_SERVER['REMOTE_ADDR']) != null,
+                IpDb::find($_SERVER['REMOTE_ADDR']) != null,
             );
         }
     }
@@ -103,9 +100,9 @@ if (!function_exists('set_user_session_location')) {
     /**
      * Set user country in session variable, for analytic purposes
      */
-    function set_user_session_location()
+    function set_user_session_location(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         $location = [
@@ -118,7 +115,7 @@ if (!function_exists('set_user_session_location')) {
         // Finds location:
         if (!$session->has('location')) {
             try {
-                $cityReader = new \GeoIp2\Database\Reader(
+                $cityReader = new Reader(
                     WRITEPATH . 'uploads/GeoLite2-City/GeoLite2-City.mmdb',
                 );
                 $city = $cityReader->city($_SERVER['REMOTE_ADDR']);
@@ -133,8 +130,8 @@ if (!function_exists('set_user_session_location')) {
                     'latitude' => round($city->location->latitude, 3),
                     'longitude' => round($city->location->longitude, 3),
                 ];
-            } catch (\Exception $e) {
                 // If things go wrong the show must go on and the user must be able to download the file
+            } catch (Exception $exception) {
             }
             $session->set('location', $location);
         }
@@ -145,9 +142,9 @@ if (!function_exists('set_user_session_player')) {
     /**
      * Set user player in session variable, for analytic purposes
      */
-    function set_user_session_player()
+    function set_user_session_player(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         if (!$session->has('player')) {
@@ -155,11 +152,9 @@ if (!function_exists('set_user_session_player')) {
             $userAgent = $_SERVER['HTTP_USER_AGENT'];
 
             try {
-                $playerFound = \Opawg\UserAgentsPhp\UserAgents::find(
-                    $userAgent,
-                );
-            } catch (\Exception $e) {
+                $playerFound = UserAgents::find($userAgent);
                 // If things go wrong the show must go on and the user must be able to download the file
+            } catch (Exception $exception) {
             }
             if ($playerFound) {
                 $session->set('player', $playerFound);
@@ -172,16 +167,16 @@ if (!function_exists('set_user_session_player')) {
                 ]);
                 // Add to unknown list
                 try {
-                    $db = \Config\Database::connect();
+                    $db = Database::connect();
                     $procedureNameAnalyticsUnknownUseragents = $db->prefixTable(
                         'analytics_unknown_useragents',
                     );
                     $db->query(
-                        "CALL $procedureNameAnalyticsUnknownUseragents(?)",
+                        "CALL {$procedureNameAnalyticsUnknownUseragents}(?)",
                         [$userAgent],
                     );
-                } catch (\Exception $e) {
                     // If things go wrong the show must go on and the user must be able to download the file
+                } catch (Exception $exception) {
                 }
             }
         }
@@ -191,20 +186,18 @@ if (!function_exists('set_user_session_player')) {
 if (!function_exists('set_user_session_browser')) {
     /**
      * Set user browser in session variable, for analytic purposes
-     *
-     * @return void
      */
-    function set_user_session_browser()
+    function set_user_session_browser(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         if (!$session->has('browser')) {
             $browserName = '- Other -';
             try {
-                $whichbrowser = new \WhichBrowser\Parser(getallheaders());
+                $whichbrowser = new Parser(getallheaders());
                 $browserName = $whichbrowser->browser->name;
-            } catch (\Exception $e) {
+            } catch (Exception $exception) {
                 $browserName = '- Could not get browser name -';
             }
             if ($browserName == null) {
@@ -218,23 +211,21 @@ if (!function_exists('set_user_session_browser')) {
 if (!function_exists('set_user_session_referer')) {
     /**
      * Set user referer in session variable, for analytic purposes
-     *
-     * @return void
      */
-    function set_user_session_referer()
+    function set_user_session_referer(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         $newreferer = isset($_SERVER['HTTP_REFERER'])
             ? $_SERVER['HTTP_REFERER']
             : '- Direct -';
         $newreferer =
-            parse_url($newreferer, PHP_URL_HOST) ==
+            parse_url($newreferer, PHP_URL_HOST) ===
             parse_url(current_url(false), PHP_URL_HOST)
                 ? '- Direct -'
                 : $newreferer;
-        if (!$session->has('referer') or $newreferer != '- Direct -') {
+        if (!$session->has('referer') || $newreferer != '- Direct -') {
             $session->set('referer', $newreferer);
         }
     }
@@ -243,12 +234,10 @@ if (!function_exists('set_user_session_referer')) {
 if (!function_exists('set_user_session_entry_page')) {
     /**
      * Set user entry page in session variable, for analytic purposes
-     *
-     * @return void
      */
-    function set_user_session_entry_page()
+    function set_user_session_entry_page(): void
     {
-        $session = \Config\Services::session();
+        $session = Services::session();
         $session->start();
 
         $entryPage = $_SERVER['REQUEST_URI'];
@@ -279,19 +268,17 @@ if (!function_exists('podcast_hit')) {
      * @param integer $bytesThreshold The minimum total number of bytes that must be downloaded so that an episode is counted (>1mn)
      * @param integer $fileSize The podcast complete file size
      * @param string $serviceName The name of the service that had fetched the RSS feed
-     *
-     * @return void
      */
     function podcast_hit(
-        $podcastId,
-        $episodeId,
-        $bytesThreshold,
-        $fileSize,
+        int $podcastId,
+        int $episodeId,
+        int $bytesThreshold,
+        int $fileSize,
         $duration,
         $publicationDate,
-        $serviceName
-    ) {
-        $session = \Config\Services::session();
+        string $serviceName
+    ): void {
+        $session = Services::session();
         $session->start();
 
         // We try to count (but if things went wrong the show should go on and the user should be able to download the file):
@@ -333,19 +320,16 @@ if (!function_exists('podcast_hit')) {
                 // If HTTP_RANGE is null we are downloading the complete file:
                 if (!$httpRange) {
                     $downloadedBytes = $fileSize;
-                } else {
+                } elseif ($httpRange != 'bytes=0-1') {
                     // [0-1] bytes range requests are used (by Apple) to check that file exists and that 206 partial content is working.
-                    // We don't count these requests:
-                    if ($httpRange != 'bytes=0-1') {
-                        // We calculate how many bytes are being downloaded based on HTTP_RANGE values:
-                        $ranges = explode(',', substr($httpRange, 6));
-                        foreach ($ranges as $range) {
-                            $parts = explode('-', $range);
-                            $downloadedBytes += empty($parts[1])
-                                ? $fileSize
-                                : $parts[1] -
-                                    (empty($parts[0]) ? 0 : $parts[0]);
-                        }
+                    // We don't count these requests.
+                    // We calculate how many bytes are being downloaded based on HTTP_RANGE values:
+                    $ranges = explode(',', substr($httpRange, 6));
+                    foreach ($ranges as $range) {
+                        $parts = explode('-', $range);
+                        $downloadedBytes += empty($parts[1])
+                            ? $fileSize
+                            : $parts[1] - (empty($parts[0]) ? 0 : $parts[0]);
                     }
                 }
                 // We save the number of downloaded bytes for this user and this episode:
@@ -353,7 +337,7 @@ if (!function_exists('podcast_hit')) {
 
                 // If more that 1mn was downloaded, that's a hit, we send that to the database:
                 if ($downloadedBytes >= $bytesThreshold) {
-                    $db = \Config\Database::connect();
+                    $db = Database::connect();
                     $procedureName = $db->prefixTable('analytics_podcasts');
 
                     $age = intdiv(time() - $publicationDate, 86400);
@@ -374,7 +358,7 @@ if (!function_exists('podcast_hit')) {
                     // We add one download
                     if ($downloadsByUser) {
                         $newListener = 0;
-                        $downloadsByUser++;
+                        ++$downloadsByUser;
                     } else {
                         $downloadsByUser = 1;
                     }
@@ -388,7 +372,7 @@ if (!function_exists('podcast_hit')) {
                     );
 
                     $db->query(
-                        "CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
+                        "CALL {$procedureName}(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
                         [
                             $podcastId,
                             $episodeId,
@@ -409,9 +393,9 @@ if (!function_exists('podcast_hit')) {
                     );
                 }
             }
-        } catch (\Exception $e) {
+        } catch (Exception $exception) {
             // If things go wrong the show must go on and the user must be able to download the file
-            log_message('critical', $e);
+            log_message('critical', $exception);
         }
     }
 }
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php
index 13b9fdf8c2..d5661deb42 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByCountry;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByCountryModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_country';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByCountry::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByCountry::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets country data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByCountry[]
      */
     public function getDataWeekly(int $podcastId): array
     {
@@ -54,15 +63,14 @@ class AnalyticsPodcastByCountryModel extends Model
                 600,
             );
         }
+
         return $found;
     }
 
     /**
      * Gets country data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByCountry[]
      */
     public function getDataYearly(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php
index ed53ce2270..075642873a 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php
@@ -10,24 +10,32 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByEpisode;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByEpisodeModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_episode';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByEpisode::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByEpisode::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
-     * @param int $podcastId
-     * @param int $episodeId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByEpisode[]
      */
     public function getDataByDay(int $podcastId, int $episodeId): array
     {
@@ -53,14 +61,12 @@ class AnalyticsPodcastByEpisodeModel extends Model
                 600,
             );
         }
+
         return $found;
     }
 
     /**
-     * @param int $podcastId
-     * @param int $episodeId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByEpisode[]
      */
     public function getDataByMonth(int $podcastId, int $episodeId = null): array
     {
@@ -85,6 +91,7 @@ class AnalyticsPodcastByEpisodeModel extends Model
                 600,
             );
         }
+
         return $found;
     }
 }
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php
index 4b5df27044..34a5d18f7c 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php
@@ -10,32 +10,39 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByHour;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByHourModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_hour';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByHour::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByHour::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets hits data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByHour[]
      */
     public function getData(int $podcastId): array
     {
         if (!($found = cache("{$podcastId}_analytics_podcasts_by_hour"))) {
-            $found = $this->select(
-                'right(concat(\'0\',hour,\'h\'),3) as labels',
-            )
+            $found = $this->select("right(concat('0',hour,'h'),3) as labels")
                 ->selectSum('hits', 'values')
                 ->where([
                     'podcast_id' => $podcastId,
@@ -51,6 +58,7 @@ class AnalyticsPodcastByHourModel extends Model
                 600,
             );
         }
+
         return $found;
     }
 }
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php
index 237843448e..636f2cbbc8 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByPlayer;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByPlayerModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_player';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByPlayer::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByPlayer::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets player data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByPlayer[]
      */
     public function getDataByAppWeekly(int $podcastId): array
     {
@@ -61,9 +70,7 @@ class AnalyticsPodcastByPlayerModel extends Model
     /**
      * Gets player data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByPlayer[]
      */
     public function getDataByAppYearly(int $podcastId): array
     {
@@ -96,9 +103,7 @@ class AnalyticsPodcastByPlayerModel extends Model
     /**
      * Gets os data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByPlayer[]
      */
     public function getDataByOsWeekly(int $podcastId): array
     {
@@ -132,9 +137,7 @@ class AnalyticsPodcastByPlayerModel extends Model
     /**
      * Gets player data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByPlayer[]
      */
     public function getDataByDeviceWeekly(int $podcastId): array
     {
@@ -167,9 +170,7 @@ class AnalyticsPodcastByPlayerModel extends Model
     /**
      * Gets bots data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByPlayer[]
      */
     public function getDataBots(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php
index 76a2f2dcf9..b40d606daf 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByRegion;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByRegionModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_region';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByRegion::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByRegion::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets region data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByRegion[]
      */
     public function getData(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php
index a3039ae936..6bd889c44d 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcastsByService;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastByServiceModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts_by_player';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcastsByService::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcastsByService::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets service data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcastsByService[]
      */
     public function getDataByServiceWeekly(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php
index a621bae20f..dccf6842f7 100644
--- a/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsPodcasts;
 use CodeIgniter\Model;
 
 class AnalyticsPodcastModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_podcasts';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsPodcasts::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsPodcasts::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets hits data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataByDay(int $podcastId): array
     {
@@ -49,9 +58,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets hits data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataByWeekday(int $podcastId): array
     {
@@ -80,9 +87,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets bandwidth data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataBandwidthByDay(int $podcastId): array
     {
@@ -109,9 +114,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets hits data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataByMonth(int $podcastId): array
     {
@@ -137,9 +140,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets unique listeners data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataUniqueListenersByDay(int $podcastId): array
     {
@@ -168,9 +169,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets unique listeners data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataUniqueListenersByMonth(int $podcastId): array
     {
@@ -200,9 +199,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets listening-time data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataTotalListeningTimeByDay(int $podcastId): array
     {
@@ -233,9 +230,7 @@ class AnalyticsPodcastModel extends Model
     /**
      * Gets listening-time data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsPodcasts[]
      */
     public function getDataTotalListeningTimeByMonth(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php
index c002668beb..c76772f652 100644
--- a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php
@@ -10,17 +10,31 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsUnknownUseragents;
 use CodeIgniter\Model;
 
 class AnalyticsUnknownUseragentsModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_unknown_useragents';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsUnknownUseragents::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsUnknownUseragents::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 }
diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php
index efdfc95f1f..aadfb01b0a 100644
--- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsWebsiteByBrowser;
 use CodeIgniter\Model;
 
 class AnalyticsWebsiteByBrowserModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_website_by_browser';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsWebsiteByBrowser::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsWebsiteByBrowser::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets browser data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsWebsiteByBrowser[]
      */
     public function getData(int $podcastId): array
     {
diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php
index 95931b426e..8981e8099a 100644
--- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php
@@ -10,32 +10,41 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsWebsiteByEntryPage;
 use CodeIgniter\Model;
 
 class AnalyticsWebsiteByEntryPageModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_website_by_entry_page';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsWebsiteByEntryPage::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsWebsiteByEntryPage::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets entry pages data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsWebsiteByEntryPage[]
      */
     public function getData(int $podcastId): array
     {
         if (!($found = cache("{$podcastId}_analytics_website_by_entry_page"))) {
             $oneWeekAgo = date('Y-m-d', strtotime('-1 week'));
             $found = $this->select(
-                'IF(entry_page_url=\'/\',\'/\',SUBSTRING_INDEX(entry_page_url,\'/\',-1)) as labels',
+                "IF(entry_page_url='/','/',SUBSTRING_INDEX(entry_page_url,'/',-1)) as labels",
             )
                 ->selectSum('hits', 'values')
                 ->where([
diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php
index 65ee8dd2be..507ee56ccb 100644
--- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php
+++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php
@@ -10,25 +10,34 @@
 
 namespace Analytics\Models;
 
+use Analytics\Entities\AnalyticsWebsiteByReferer;
 use CodeIgniter\Model;
 
 class AnalyticsWebsiteByRefererModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_website_by_referer';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \Analytics\Entities\AnalyticsWebsiteByReferer::class;
+    /**
+     * @var string
+     */
+    protected $returnType = AnalyticsWebsiteByReferer::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     /**
      * Gets referer data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsWebsiteByReferer[]
      */
     public function getData(int $podcastId): array
     {
@@ -55,9 +64,7 @@ class AnalyticsWebsiteByRefererModel extends Model
     /**
      * Gets domain data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsWebsiteByReferer[]
      */
     public function getDataByDomainWeekly(int $podcastId): array
     {
@@ -65,9 +72,7 @@ class AnalyticsWebsiteByRefererModel extends Model
             !($found = cache("{$podcastId}_analytics_website_by_domain_weekly"))
         ) {
             $oneWeekAgo = date('Y-m-d', strtotime('-1 week'));
-            $found = $this->select(
-                'SUBSTRING_INDEX(domain, \'.\', -2) as labels',
-            )
+            $found = $this->select("SUBSTRING_INDEX(domain, '.', -2) as labels")
                 ->selectSum('hits', 'values')
                 ->where([
                     'podcast_id' => $podcastId,
@@ -88,9 +93,7 @@ class AnalyticsWebsiteByRefererModel extends Model
     /**
      * Gets domain data for a podcast
      *
-     * @param int $podcastId
-     *
-     * @return array
+     * @return AnalyticsWebsiteByReferer[]
      */
     public function getDataByDomainYearly(int $podcastId): array
     {
@@ -98,9 +101,7 @@ class AnalyticsWebsiteByRefererModel extends Model
             !($found = cache("{$podcastId}_analytics_website_by_domain_yearly"))
         ) {
             $oneYearAgo = date('Y-m-d', strtotime('-1 year'));
-            $found = $this->select(
-                'SUBSTRING_INDEX(domain, \'.\', -2) as labels',
-            )
+            $found = $this->select("SUBSTRING_INDEX(domain, '.', -2) as labels")
                 ->selectSum('hits', 'values')
                 ->where([
                     'podcast_id' => $podcastId,
diff --git a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php b/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php
index 1f531f456d..b0173a5840 100644
--- a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php
+++ b/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php
@@ -14,10 +14,11 @@ use CodeIgniter\Model;
 
 class UnknownUserAgentsModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'analytics_unknown_useragents';
 
-    protected $allowedFields = [];
-
     public function getUserAgents($last_known_id = 0)
     {
         return $this->where('id >', $last_known_id)->findAll();
diff --git a/app/Libraries/Breadcrumb.php b/app/Libraries/Breadcrumb.php
index 5f5bea273a..e520b3987b 100644
--- a/app/Libraries/Breadcrumb.php
+++ b/app/Libraries/Breadcrumb.php
@@ -32,12 +32,12 @@ class Breadcrumb
         $uri = '';
         foreach (current_url(true)->getSegments() as $segment) {
             $uri .= '/' . $segment;
-            array_push($this->links, [
+            $this->links[] = [
                 'text' => is_numeric($segment)
                     ? $segment
                     : lang('Breadcrumb.' . $segment),
                 'href' => base_url($uri),
-            ]);
+            ];
         }
     }
 
@@ -57,10 +57,8 @@ class Breadcrumb
      * replaceParams($newParams);
      *
      * The breadcrumb is now `Home / podcasts / foo / episodes / bar`
-     *
-     * @param array $newParams
      */
-    public function replaceParams($newParams)
+    public function replaceParams(array $newParams): void
     {
         foreach ($this->links as $key => $link) {
             if (is_numeric($link['text'])) {
@@ -72,10 +70,8 @@ class Breadcrumb
 
     /**
      * Renders the breadcrumb object as an accessible html breadcrumb nav
-     *
-     * @return string
      */
-    public function render($class = null)
+    public function render($class = null): string
     {
         $listItems = '';
         $keys = array_keys($this->links);
diff --git a/app/Libraries/Image.php b/app/Libraries/Image.php
index 9204aa3846..572c15648d 100644
--- a/app/Libraries/Image.php
+++ b/app/Libraries/Image.php
@@ -8,67 +8,69 @@
 
 namespace App\Libraries;
 
+use Config\Images;
+use Config\Services;
 class Image
 {
     /**
-     * @var \Config\Images
+     * @var string
      */
-    protected $config;
+    public $original_url;
 
     /**
      * @var string
      */
-    protected $original_path;
+    public $mimetype;
 
     /**
      * @var string
      */
-    public $original_url;
+    public $thumbnail_url;
 
     /**
      * @var string
      */
-    protected $thumbnail_path;
+    public $medium_url;
 
     /**
      * @var string
      */
-    public $thumbnail_url;
+    public $large_url;
 
     /**
      * @var string
      */
-    protected $medium_path;
+    public $feed_path;
 
     /**
      * @var string
      */
-    public $medium_url;
+    public $feed_url;
 
     /**
      * @var string
      */
-    protected $large_path;
-
+    public $id3_path;
+    /**
+     * @var Images
+     */
+    protected $config;
     /**
      * @var string
      */
-    public $large_url;
-
+    protected $original_path;
     /**
      * @var string
      */
-    public $feed_path;
-
+    protected $thumbnail_path;
     /**
      * @var string
      */
-    public $feed_url;
-
+    protected $medium_path;
     /**
      * @var string
      */
-    public $id3_path;
+    protected $large_path;
 
     public function __construct($originalPath, $mimetype)
     {
@@ -115,7 +117,7 @@ class Image
         $this->mimetype = $mimetype;
     }
 
-    public function saveSizes()
+    public function saveSizes(): void
     {
         $thumbnailSize = $this->config->thumbnailSize;
         $mediumSize = $this->config->mediumSize;
@@ -123,7 +125,7 @@ class Image
         $feedSize = $this->config->feedSize;
         $id3Size = $this->config->id3Size;
 
-        $imageService = \Config\Services::image();
+        $imageService = Services::image();
 
         $imageService
             ->withFile($this->original_path)
diff --git a/app/Libraries/NoteObject.php b/app/Libraries/NoteObject.php
index 4602784e25..f02c9275b1 100644
--- a/app/Libraries/NoteObject.php
+++ b/app/Libraries/NoteObject.php
@@ -11,7 +11,7 @@ namespace App\Libraries;
 class NoteObject extends \ActivityPub\Objects\NoteObject
 {
     /**
-     * @param \App\Entities\Note $note
+     * @param Note $note
      */
     public function __construct($note)
     {
diff --git a/app/Libraries/PodcastActor.php b/app/Libraries/PodcastActor.php
index acc55e2ce4..5f6351e110 100644
--- a/app/Libraries/PodcastActor.php
+++ b/app/Libraries/PodcastActor.php
@@ -8,6 +8,7 @@
 
 namespace App\Libraries;
 
+use ActivityPub\Entities\Actor;
 use App\Models\PodcastModel;
 
 class PodcastActor extends \ActivityPub\Objects\ActorObject
@@ -17,10 +18,7 @@ class PodcastActor extends \ActivityPub\Objects\ActorObject
      */
     protected $rss;
 
-    /**
-     * @param \App\Entities\Actor $actor
-     */
-    public function __construct($actor)
+    public function __construct(Actor $actor)
     {
         parent::__construct($actor);
 
diff --git a/app/Libraries/Router.php b/app/Libraries/Router.php
index fddaa2ba4f..d8202b5ca5 100644
--- a/app/Libraries/Router.php
+++ b/app/Libraries/Router.php
@@ -29,6 +29,7 @@ class Router extends \CodeIgniter\Router\Router
      */
     protected function checkRoutes(string $uri): bool
     {
+        /** @noRector RemoveExtraParametersRector */
         $routes = $this->collection->getRoutes(
             $this->collection->getHTTPVerb(),
         );
@@ -54,7 +55,7 @@ class Router extends \CodeIgniter\Router\Router
                 $localeSegment = array_search(
                     '{locale}',
                     preg_split(
-                        '/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m',
+                        '~[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+~m',
                         $key,
                     ),
                     true,
diff --git a/app/Libraries/SimpleRSSElement.php b/app/Libraries/SimpleRSSElement.php
index f37453c17a..e39c0359dc 100644
--- a/app/Libraries/SimpleRSSElement.php
+++ b/app/Libraries/SimpleRSSElement.php
@@ -16,11 +16,16 @@ class SimpleRSSElement extends SimpleXMLElement
      * Adds a child with $value inside CDATA
      *
      * @param string $name — The name of the child element to add.
-     * @param string $value — [optional] If specified, the value of the child element.
-     * @param string $namespace [optional] If specified, the namespace to which the child element belongs.
+     * @param string|null $value — [optional] If specified, the value of the child element.
+     * @param string|null $namespace [optional] If specified, the namespace to which the child element belongs.
+     *
+     * @return $this
      */
-    public function addChildWithCDATA($name, $value = null, $namespace = null)
-    {
+    public function addChildWithCDATA(
+        string $name,
+        ?string $value = null,
+        ?string $namespace = null
+    ) {
         $new_child = parent::addChild($name, null, $namespace);
 
         if ($new_child !== null) {
@@ -39,6 +44,7 @@ class SimpleRSSElement extends SimpleXMLElement
      * @param string $name — The name of the child element to add.
      * @param string $value — [optional] If specified, the value of the child element.
      * @param string $namespace [optional] If specified, the namespace to which the child element belongs.
+     * @return $this
      */
     public function addChild($name, $value = null, $namespace = null)
     {
diff --git a/app/Models/CategoryModel.php b/app/Models/CategoryModel.php
index 1116116b23..0bf5043626 100644
--- a/app/Models/CategoryModel.php
+++ b/app/Models/CategoryModel.php
@@ -8,13 +8,23 @@
 
 namespace App\Models;
 
+use App\Entities\Category;
 use CodeIgniter\Model;
 
 class CategoryModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'categories';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'parent_id',
         'code',
@@ -22,12 +32,21 @@ class CategoryModel extends Model
         'google_category',
     ];
 
-    protected $returnType = \App\Entities\Category::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Category::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
-    public function getCategoryById($id)
+    public function getCategoryById($id): ?Category
     {
         return $this->find($id);
     }
@@ -60,12 +79,9 @@ class CategoryModel extends Model
     /**
      * Sets categories for a given podcast
      *
-     * @param int $podcastId
-     * @param array $categories
-     *
-     * @return integer|false Number of rows inserted or FALSE on failure
+     * @return int|bool Number of rows inserted or FALSE on failure
      */
-    public function setPodcastCategories($podcastId, $categories)
+    public function setPodcastCategories(int $podcastId, ?array $categories)
     {
         cache()->delete("podcast#{$podcastId}_categories");
 
@@ -74,37 +90,35 @@ class CategoryModel extends Model
             ->table('podcasts_categories')
             ->delete(['podcast_id' => $podcastId]);
 
-        if (!empty($categories)) {
-            // prepare data for `podcasts_categories` table
-            $data = array_reduce(
-                $categories,
-                function ($result, $categoryId) use ($podcastId) {
-                    $result[] = [
-                        'podcast_id' => $podcastId,
-                        'category_id' => $categoryId,
-                    ];
-
-                    return $result;
-                },
-                [],
-            );
-
-            // Set podcast categories
-            return $this->db->table('podcasts_categories')->insertBatch($data);
+        if (empty($categories)) {
+            // no row has been inserted after deletion
+            return 0;
         }
 
-        // no row has been inserted after deletion
-        return 0;
+        // prepare data for `podcasts_categories` table
+        $data = array_reduce(
+            $categories,
+            function ($result, $categoryId) use ($podcastId) {
+                $result[] = [
+                    'podcast_id' => $podcastId,
+                    'category_id' => $categoryId,
+                ];
+
+                return $result;
+            },
+            [],
+        );
+
+        // Set podcast categories
+        return $this->db->table('podcasts_categories')->insertBatch($data);
     }
 
     /**
      * Gets all the podcast categories
      *
-     * @param int $podcastId
-     *
-     * @return \App\Entities\Category[]
+     * @return Category[]
      */
-    public function getPodcastCategories($podcastId)
+    public function getPodcastCategories(int $podcastId): array
     {
         $cacheName = "podcast#{$podcastId}_categories";
         if (!($categories = cache($cacheName))) {
diff --git a/app/Models/CreditModel.php b/app/Models/CreditModel.php
index 00121757bf..dfded4b217 100644
--- a/app/Models/CreditModel.php
+++ b/app/Models/CreditModel.php
@@ -8,13 +8,18 @@
 
 namespace App\Models;
 
+use App\Entities\Credit;
 use CodeIgniter\Model;
 
 class CreditModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'credits';
 
-    protected $allowedFields = [];
-
-    protected $returnType = \App\Entities\Credit::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Credit::class;
 }
diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php
index 9679d717c5..c76aaa20ce 100644
--- a/app/Models/EpisodeModel.php
+++ b/app/Models/EpisodeModel.php
@@ -8,13 +8,55 @@
 
 namespace App\Models;
 
+use App\Entities\Episode;
 use CodeIgniter\Model;
 
 class EpisodeModel extends Model
 {
+    // TODO: remove
+    /**
+     * @var array<string, array<string, string>>
+     */
+    public static $themes = [
+        'light-transparent' => [
+            'style' =>
+                'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
+            'background' => 'transparent',
+            'text' => '#000',
+            'inverted' => '#fff',
+        ],
+        'light' => [
+            'style' => 'background-color: #fff;',
+            'background' => '#fff',
+            'text' => '#000',
+            'inverted' => '#fff',
+        ],
+        'dark-transparent' => [
+            'style' =>
+                'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
+            'background' => 'transparent',
+            'text' => '#fff',
+            'inverted' => '#000',
+        ],
+        'dark' => [
+            'style' => 'background-color: #001f1a;',
+            'background' => '#001f1a',
+            'text' => '#fff',
+            'inverted' => '#000',
+        ],
+    ];
+    /**
+     * @var string
+     */
     protected $table = 'episodes';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'podcast_id',
@@ -51,11 +93,23 @@ class EpisodeModel extends Model
         'updated_by',
     ];
 
-    protected $returnType = \App\Entities\Episode::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Episode::class;
 
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = true;
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'podcast_id' => 'required',
         'title' => 'required',
@@ -71,53 +125,32 @@ class EpisodeModel extends Model
         'created_by' => 'required',
         'updated_by' => 'required',
     ];
-    protected $validationMessages = [];
 
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['writeEnclosureMetadata', 'clearCache'];
     // clear cache beforeUpdate because if slug changes, so will the episode link
+    /**
+     * @var string[]
+     */
     protected $beforeUpdate = ['clearCache'];
-    protected $afterUpdate = ['writeEnclosureMetadata'];
-    protected $beforeDelete = ['clearCache'];
 
-    // TODO: remove
-    public static $themes = [
-        'light-transparent' => [
-            'style' =>
-                'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
-            'background' => 'transparent',
-            'text' => '#000',
-            'inverted' => '#fff',
-        ],
-        'light' => [
-            'style' => 'background-color: #fff;',
-            'background' => '#fff',
-            'text' => '#000',
-            'inverted' => '#fff',
-        ],
-        'dark-transparent' => [
-            'style' =>
-                'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
-            'background' => 'transparent',
-            'text' => '#fff',
-            'inverted' => '#000',
-        ],
-        'dark' => [
-            'style' => 'background-color: #001f1a;',
-            'background' => '#001f1a',
-            'text' => '#fff',
-            'inverted' => '#000',
-        ],
-    ];
+    /**
+     * @var string[]
+     */
+    protected $afterUpdate = ['writeEnclosureMetadata'];
 
     /**
-     *
-     * @param int|string $podcastId Podcast Id or name
-     * @param mixed $episodeSlug
-     * @return mixed
+     * @var string[]
      */
-    public function getEpisodeBySlug($podcastId, $episodeSlug)
-    {
-        $cacheName = "podcast#{$podcastId}_episode@{$episodeSlug}";
+    protected $beforeDelete = ['clearCache'];
+
+    public function getEpisodeBySlug(
+        int $podcastId,
+        string $episodeSlug
+    ): ?Episode {
+        $cacheName = "podcast#{$podcastId}_episode-{$episodeSlug}";
         if (!($found = cache($cacheName))) {
             $builder = $this->select('episodes.*')
                 ->where('slug', $episodeSlug)
@@ -179,9 +212,7 @@ class EpisodeModel extends Model
      * Gets all episodes for a podcast ordered according to podcast type
      * Filtered depending on year or season
      *
-     * @param int $podcastId
-     *
-     * @return \App\Entities\Episode[]
+     * @return Episode[]
      */
     public function getPodcastEpisodes(
         int $podcastId,
@@ -244,8 +275,6 @@ class EpisodeModel extends Model
      * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp
      * Returns false if there's no episode to publish
      *
-     * @param int $podcastId
-     *
      * @return int|false seconds
      */
     public function getSecondsToNextUnpublishedEpisode(int $podcastId)
@@ -261,23 +290,13 @@ class EpisodeModel extends Model
             ->get()
             ->getResultArray();
 
-        return (int) $result ? $result[0]['timestamp_diff'] : false;
+        return (int) $result !== 0 ? $result[0]['timestamp_diff'] : false;
     }
 
-    protected function writeEnclosureMetadata(array $data)
-    {
-        helper('id3');
-
-        $episode = (new EpisodeModel())->find(
-            is_array($data['id']) ? $data['id'][0] : $data['id'],
-        );
-
-        write_audio_file_tags($episode);
-
-        return $data;
-    }
-
-    public function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    public function clearCache($data): array
     {
         $episode = (new EpisodeModel())->find(
             is_array($data['id']) ? $data['id'][0] : $data['id'],
@@ -294,7 +313,7 @@ class EpisodeModel extends Model
             "podcast#{$episode->podcast_id}_episode#{$episode->id}*",
         );
         cache()->delete(
-            "podcast#{$episode->podcast_id}_episode@{$episode->slug}",
+            "podcast#{$episode->podcast_id}_episode-{$episode->slug}",
         );
 
         cache()->deleteMatching(
@@ -324,4 +343,20 @@ class EpisodeModel extends Model
 
         return $data;
     }
+
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    protected function writeEnclosureMetadata(array $data): array
+    {
+        helper('id3');
+
+        $episode = (new EpisodeModel())->find(
+            is_array($data['id']) ? $data['id'][0] : $data['id'],
+        );
+
+        write_audio_file_tags($episode);
+
+        return $data;
+    }
 }
diff --git a/app/Models/EpisodePersonModel.php b/app/Models/EpisodePersonModel.php
index 5e96171b6e..1fd56a5363 100644
--- a/app/Models/EpisodePersonModel.php
+++ b/app/Models/EpisodePersonModel.php
@@ -8,13 +8,24 @@
 
 namespace App\Models;
 
+use CodeIgniter\Database\BaseResult;
+use App\Entities\EpisodePerson;
 use CodeIgniter\Model;
 
 class EpisodePersonModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'episodes_persons';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'podcast_id',
@@ -24,18 +35,35 @@ class EpisodePersonModel extends Model
         'person_role',
     ];
 
-    protected $returnType = \App\Entities\EpisodePerson::class;
+    /**
+     * @var string
+     */
+    protected $returnType = EpisodePerson::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'episode_id' => 'required',
         'person_id' => 'required',
     ];
-    protected $validationMessages = [];
 
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['clearCache'];
+    /**
+     * @var string[]
+     */
     protected $beforeDelete = ['clearCache'];
 
     public function getEpisodePersons($podcastId, $episodeId)
@@ -57,17 +85,14 @@ class EpisodePersonModel extends Model
      * Add persons to episode
      *
      * @param int podcastId
-     * @param int $episodeId
-     * @param array $persons
-     * @param array $groups_roles
      *
-     * @return integer|false Number of rows inserted or FALSE on failure
+     * @return bool|int Number of rows inserted or FALSE on failure
      */
     public function addEpisodePersons(
         $podcastId,
-        $episodeId,
-        $persons,
-        $groups_roles
+        int $episodeId,
+        array $persons,
+        array $groups_roles
     ) {
         if (!empty($persons)) {
             $this->clearCache([
@@ -76,7 +101,7 @@ class EpisodePersonModel extends Model
 
             $data = [];
             foreach ($persons as $person) {
-                if ($groups_roles) {
+                if ($groups_roles !== []) {
                     foreach ($groups_roles as $group_role) {
                         $group_role = explode(',', $group_role);
                         $data[] = [
@@ -100,6 +125,9 @@ class EpisodePersonModel extends Model
         return 0;
     }
 
+    /**
+     * @return bool|BaseResult
+     */
     public function removeEpisodePersons(
         $podcastId,
         $episodeId,
@@ -112,9 +140,11 @@ class EpisodePersonModel extends Model
         ]);
     }
 
-    protected function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    protected function clearCache(array $data): array
     {
-        $episodeId = null;
         if (isset($data['episode_id'])) {
             $episodeId = $data['episode_id'];
         } else {
diff --git a/app/Models/LanguageModel.php b/app/Models/LanguageModel.php
index 5eb780f386..09c52dfb92 100644
--- a/app/Models/LanguageModel.php
+++ b/app/Models/LanguageModel.php
@@ -8,18 +8,37 @@
 
 namespace App\Models;
 
+use App\Entities\Language;
 use CodeIgniter\Model;
 
 class LanguageModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'languages';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['code', 'native_name'];
 
-    protected $returnType = \App\Entities\Language::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Language::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     public function getLanguageOptions()
@@ -33,7 +52,7 @@ class LanguageModel extends Model
                     $result[$language->code] = $language->native_name;
                     return $result;
                 },
-                []
+                [],
             );
 
             cache()->save('language_options', $options, DECADE);
diff --git a/app/Models/PageModel.php b/app/Models/PageModel.php
index c045ca0c70..f52eaf9438 100644
--- a/app/Models/PageModel.php
+++ b/app/Models/PageModel.php
@@ -8,34 +8,70 @@
 
 namespace App\Models;
 
+use App\Entities\Page;
 use CodeIgniter\Model;
 
 class PageModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'pages';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['id', 'title', 'slug', 'content'];
 
-    protected $returnType = \App\Entities\Page::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Page::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = true;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'title' => 'required',
         'slug' =>
             'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]',
         'content' => 'required',
     ];
-    protected $validationMessages = [];
 
-    // Before update because slug or title might change
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['clearCache'];
+
+    /**
+     * Before update because slug or title might change
+     *
+     * @var string[]
+     */
     protected $beforeUpdate = ['clearCache'];
+
+    /**
+     * @var string[]
+     */
     protected $beforeDelete = ['clearCache'];
 
-    protected function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    protected function clearCache(array $data): array
     {
         // Clear the cache of all pages
         cache()->deleteMatching('page*');
diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php
index 41dee4f269..94054790ed 100644
--- a/app/Models/PersonModel.php
+++ b/app/Models/PersonModel.php
@@ -8,13 +8,23 @@
 
 namespace App\Models;
 
+use App\Entities\Person;
 use CodeIgniter\Model;
 
 class PersonModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'persons';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'full_name',
@@ -26,11 +36,23 @@ class PersonModel extends Model
         'updated_by',
     ];
 
-    protected $returnType = \App\Entities\Person::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Person::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'full_name' => 'required',
         'unique_name' =>
@@ -39,11 +61,22 @@ class PersonModel extends Model
         'created_by' => 'required',
         'updated_by' => 'required',
     ];
-    protected $validationMessages = [];
 
-    // clear cache before update if by any chance, the person name changes, so will the person link
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['clearCache'];
+
+    /**
+     * clear cache before update if by any chance, the person name changes, so will the person link
+     *
+     * @var string[]
+     */
     protected $beforeUpdate = ['clearCache'];
+
+    /**
+     * @var string[]
+     */
     protected $beforeDelete = ['clearCache'];
 
     public function getPersonById($personId)
@@ -65,7 +98,7 @@ class PersonModel extends Model
 
     public function createPerson($fullName, $informationUrl, $image)
     {
-        $person = new \App\Entities\Person([
+        $person = new Person([
             'full_name' => $fullName,
             'unique_name' => slugify($fullName),
             'information_url' => $informationUrl,
@@ -103,10 +136,13 @@ class PersonModel extends Model
         $locale = service('request')->getLocale();
         $cacheName = "taxonomy_options_{$locale}";
         if (!($options = cache($cacheName))) {
-            foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) {
+            foreach (
+                (array) lang('PersonsTaxonomy.persons')
+                as $group_key => $group
+            ) {
                 foreach ($group['roles'] as $role_key => $role) {
                     $options[
-                        "$group_key,$role_key"
+                        "{$group_key},{$role_key}"
                     ] = "{$group['label']}  â–¸  {$role['label']}";
                 }
             }
@@ -117,7 +153,10 @@ class PersonModel extends Model
         return $options;
     }
 
-    protected function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    protected function clearCache(array $data): array
     {
         $person = (new PersonModel())->find(
             is_array($data['id']) ? $data['id'][0] : $data['id'],
diff --git a/app/Models/PlatformModel.php b/app/Models/PlatformModel.php
index 45d84bb915..9ce8e241dc 100644
--- a/app/Models/PlatformModel.php
+++ b/app/Models/PlatformModel.php
@@ -11,13 +11,23 @@
 
 namespace App\Models;
 
+use App\Entities\Platform;
 use CodeIgniter\Model;
 
 class PlatformModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'platforms';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'slug';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'slug',
         'type',
@@ -26,9 +36,18 @@ class PlatformModel extends Model
         'submit_url',
     ];
 
-    protected $returnType = \App\Entities\Platform::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Platform::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
     public function getPlatforms()
@@ -45,7 +64,7 @@ class PlatformModel extends Model
 
     public function getPlatform($slug)
     {
-        $cacheName = "platform@{$slug}";
+        $cacheName = "platform-{$slug}";
         if (!($found = cache($cacheName))) {
             $found = $this->where('slug', $slug)->first();
             cache()->save($cacheName, $found, DECADE);
@@ -82,7 +101,7 @@ class PlatformModel extends Model
             )
                 ->join(
                     'podcasts_platforms',
-                    "podcasts_platforms.platform_slug = platforms.slug AND podcasts_platforms.podcast_id = $podcastId",
+                    "podcasts_platforms.platform_slug = platforms.slug AND podcasts_platforms.podcast_id = {$podcastId}",
                     'left',
                 )
                 ->where('platforms.type', $platformType)
@@ -119,6 +138,9 @@ class PlatformModel extends Model
         return $found;
     }
 
+    /**
+     * @return int|bool
+     */
     public function savePodcastPlatforms(
         $podcastId,
         $platformType,
@@ -130,9 +152,9 @@ class PlatformModel extends Model
         $platformsTable = $this->db->prefixTable('platforms');
 
         $deleteJoinQuery = <<<SQL
-        DELETE $podcastsPlatformsTable
-        FROM $podcastsPlatformsTable
-        INNER JOIN $platformsTable ON $platformsTable.slug = $podcastsPlatformsTable.platform_slug
+        DELETE {$podcastsPlatformsTable}
+        FROM {$podcastsPlatformsTable}
+        INNER JOIN {$platformsTable} ON {$platformsTable}.slug = {$podcastsPlatformsTable}.platform_slug
         WHERE `podcast_id` = ? AND `type` = ?
         SQL;
 
@@ -144,6 +166,9 @@ class PlatformModel extends Model
             ->insertBatch($podcastsPlatformsData);
     }
 
+    /**
+     * @return int|bool
+     */
     public function createPodcastPlatforms($podcastId, $podcastsPlatformsData)
     {
         $this->clearCache($podcastId);
@@ -154,6 +179,9 @@ class PlatformModel extends Model
             ->insertBatch($podcastsPlatformsData);
     }
 
+    /**
+     * @return bool|string
+     */
     public function removePodcastPlatform($podcastId, $platformSlug)
     {
         $this->clearCache($podcastId);
@@ -164,7 +192,7 @@ class PlatformModel extends Model
         ]);
     }
 
-    public function clearCache($podcastId)
+    public function clearCache($podcastId): void
     {
         cache()->deleteMatching("podcast#{$podcastId}_platforms_*");
 
diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php
index f8d79e00ab..98a7d939c3 100644
--- a/app/Models/PodcastModel.php
+++ b/app/Models/PodcastModel.php
@@ -82,10 +82,10 @@ class PodcastModel extends Model
 
     public function getPodcastByName($podcastName)
     {
-        $cacheName = "podcast@{$podcastName}";
+        $cacheName = "podcast-{$podcastName}";
         if (!($found = cache($cacheName))) {
             $found = $this->where('name', $podcastName)->first();
-            cache()->save("podcast@{$podcastName}", $found, DECADE);
+            cache()->save("podcast-{$podcastName}", $found, DECADE);
         }
 
         return $found;
@@ -405,7 +405,7 @@ class PodcastModel extends Model
 
         // delete model requests cache, includes feed / query / episode lists, etc.
         cache()->deleteMatching("podcast#{$podcast->id}*");
-        cache()->delete("podcast@{$podcast->name}");
+        cache()->delete("podcast-{$podcast->name}");
 
         // clear cache for every credit page
         cache()->deleteMatching('page_credits_*');
diff --git a/app/Models/PodcastPersonModel.php b/app/Models/PodcastPersonModel.php
index c111fe98b7..eedb85c942 100644
--- a/app/Models/PodcastPersonModel.php
+++ b/app/Models/PodcastPersonModel.php
@@ -8,13 +8,24 @@
 
 namespace App\Models;
 
+use CodeIgniter\Database\BaseResult;
+use App\Entities\PodcastPerson;
 use CodeIgniter\Model;
 
 class PodcastPersonModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'podcasts_persons';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'id',
         'podcast_id',
@@ -23,18 +34,36 @@ class PodcastPersonModel extends Model
         'person_role',
     ];
 
-    protected $returnType = \App\Entities\PodcastPerson::class;
+    /**
+     * @var string
+     */
+    protected $returnType = PodcastPerson::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = false;
 
+    /**
+     * @var array<string, string>
+     */
     protected $validationRules = [
         'podcast_id' => 'required',
         'person_id' => 'required',
     ];
-    protected $validationMessages = [];
 
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['clearCache'];
+
+    /**
+     * @var string[]
+     */
     protected $beforeDelete = ['clearCache'];
 
     public function getPodcastPersons($podcastId)
@@ -56,19 +85,18 @@ class PodcastPersonModel extends Model
     /**
      * Add persons to podcast
      *
-     * @param int $podcastId
-     * @param array $persons
-     * @param array $groups_roles
-     *
-     * @return integer Number of rows inserted or FALSE on failure
+     * @return bool|int Number of rows inserted or FALSE on failure
      */
-    public function addPodcastPersons($podcastId, $persons, $groups_roles)
-    {
+    public function addPodcastPersons(
+        int $podcastId,
+        array $persons,
+        array $groups_roles
+    ) {
         if (!empty($persons)) {
             $this->clearCache(['podcast_id' => $podcastId]);
             $data = [];
             foreach ($persons as $person) {
-                if ($groups_roles) {
+                if ($groups_roles !== []) {
                     foreach ($groups_roles as $group_role) {
                         $group_role = explode(',', $group_role);
                         $data[] = [
@@ -90,6 +118,9 @@ class PodcastPersonModel extends Model
         return 0;
     }
 
+    /**
+     * @return bool|BaseResult
+     */
     public function removePodcastPersons($podcastId, $podcastPersonId)
     {
         return $this->delete([
@@ -98,9 +129,11 @@ class PodcastPersonModel extends Model
         ]);
     }
 
-    protected function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    protected function clearCache(array $data): array
     {
-        $podcastId = null;
         if (isset($data['podcast_id'])) {
             $podcastId = $data['podcast_id'];
         } else {
diff --git a/app/Models/SoundbiteModel.php b/app/Models/SoundbiteModel.php
index 9f7bcbc6c1..8bdf04ead2 100644
--- a/app/Models/SoundbiteModel.php
+++ b/app/Models/SoundbiteModel.php
@@ -11,13 +11,24 @@
 
 namespace App\Models;
 
+use CodeIgniter\Database\BaseResult;
+use App\Entities\Soundbite;
 use CodeIgniter\Model;
 
 class SoundbiteModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'soundbites';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = [
         'podcast_id',
         'episode_id',
@@ -28,15 +39,36 @@ class SoundbiteModel extends Model
         'updated_by',
     ];
 
-    protected $returnType = \App\Entities\Soundbite::class;
+    /**
+     * @var string
+     */
+    protected $returnType = Soundbite::class;
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
+    /**
+     * @var string[]
+     */
     protected $afterInsert = ['clearCache'];
+    /**
+     * @var string[]
+     */
     protected $afterUpdate = ['clearCache'];
+    /**
+     * @var string[]
+     */
     protected $beforeDelete = ['clearCache'];
 
+    /**
+     * @return bool|BaseResult
+     */
     public function deleteSoundbite($podcastId, $episodeId, $soundbiteId)
     {
         return $this->delete([
@@ -49,10 +81,7 @@ class SoundbiteModel extends Model
     /**
      * Gets all soundbites for an episode
      *
-     * @param int $podcastId
-     * @param int $episodeId
-     *
-     * @return \App\Entities\Soundbite[]
+     * @return Soundbite[]
      */
     public function getEpisodeSoundbites(int $podcastId, int $episodeId): array
     {
@@ -69,7 +98,10 @@ class SoundbiteModel extends Model
         return $found;
     }
 
-    public function clearCache(array $data)
+    /**
+     * @return array<string, array<string|int, mixed>>
+     */
+    public function clearCache(array $data): array
     {
         $episode = (new EpisodeModel())->find(
             isset($data['data'])
diff --git a/app/Models/UserModel.php b/app/Models/UserModel.php
index e726b1324e..db7a3074c0 100644
--- a/app/Models/UserModel.php
+++ b/app/Models/UserModel.php
@@ -8,9 +8,13 @@
 
 namespace App\Models;
 
+use App\Entities\User;
 class UserModel extends \Myth\Auth\Models\UserModel
 {
-    protected $returnType = \App\Entities\User::class;
+    /**
+     * @var string
+     */
+    protected $returnType = User::class;
 
     public function getPodcastContributors($podcastId)
     {
diff --git a/app/Validation/FileRules.php b/app/Validation/FileRules.php
index 3e8552602e..05a7284a63 100644
--- a/app/Validation/FileRules.php
+++ b/app/Validation/FileRules.php
@@ -17,9 +17,7 @@ class FileRules extends ValidationFileRules
      * a specified allowable dimension.
      *
      * @param string|null $blank
-     * @param string      $params
      *
-     * @return boolean
      */
     public function min_dims(string $blank = null, string $params): bool
     {
@@ -59,14 +57,11 @@ class FileRules extends ValidationFileRules
     }
 
     //--------------------------------------------------------------------
-
     /**
      * Checks an uploaded file to verify that the image ratio is of 1:1
      *
      * @param string|null $blank
-     * @param string      $params
      *
-     * @return boolean
      */
     public function is_image_squared(string $blank = null, string $params): bool
     {
diff --git a/app/Validation/Rules.php b/app/Validation/Rules.php
index 94f117742d..ac352ba0a1 100644
--- a/app/Validation/Rules.php
+++ b/app/Validation/Rules.php
@@ -13,9 +13,6 @@ class Rules
     /**
      * Checks a URL to ensure it's formed correctly.
      *
-     * @param string $str
-     *
-     * @return boolean
      */
     public function validate_url(string $str = null): bool
     {
diff --git a/app/Views/_assets/modules/Charts.ts b/app/Views/_assets/modules/Charts.ts
index 08014db121..cf37414e05 100644
--- a/app/Views/_assets/modules/Charts.ts
+++ b/app/Views/_assets/modules/Charts.ts
@@ -13,20 +13,17 @@ const drawPieChart = (chartDivId: string, dataUrl: string | null): void => {
   chart.exporting.menu = new am4core.ExportMenu();
   chart.exporting.menu.align = "left";
   chart.exporting.menu.verticalAlign = "top";
+
   // Set theme
   am4core.useTheme(am4themes_material);
   chart.innerRadius = am4core.percent(10);
+
   // Add data
   chart.dataSource.url = dataUrl || "";
   chart.dataSource.parser.options.emptyAs = 0;
+
   // Add and configure Series
   const pieSeries = chart.series.push(new am4charts.PieSeries());
-  const grouper = pieSeries.plugins.push(
-    new am4plugins_sliceGrouper.SliceGrouper()
-  );
-  grouper.limit = 9;
-  grouper.groupName = "- Other -";
-  grouper.clickBehavior = "break";
   pieSeries.dataFields.value = "values";
   pieSeries.dataFields.category = "labels";
   pieSeries.slices.template.stroke = am4core.color("#ffffff");
@@ -34,6 +31,14 @@ const drawPieChart = (chartDivId: string, dataUrl: string | null): void => {
   pieSeries.slices.template.strokeOpacity = 1;
   pieSeries.labels.template.disabled = true;
   pieSeries.ticks.template.disabled = true;
+
+  const grouper = pieSeries.plugins.push(
+    new am4plugins_sliceGrouper.SliceGrouper()
+  );
+  grouper.limit = 9;
+  grouper.groupName = "- Other -";
+  grouper.clickBehavior = "break";
+
   chart.legend = new am4charts.Legend();
   chart.legend.position = "right";
   chart.legend.scrollable = true;
@@ -46,29 +51,38 @@ const drawXYChart = (chartDivId: string, dataUrl: string | null): void => {
   chart.exporting.menu = new am4core.ExportMenu();
   chart.exporting.menu.align = "right";
   chart.exporting.menu.verticalAlign = "bottom";
+
   // Set theme
   am4core.useTheme(am4themes_material);
+
   // Create axes
   const dateAxis = chart.xAxes.push(new am4charts.DateAxis());
   dateAxis.renderer.minGridDistance = 60;
   chart.yAxes.push(new am4charts.ValueAxis());
+
   // Add data
   chart.dataSource.url = dataUrl || "";
   chart.dataSource.parser.options.emptyAs = 0;
+
   // Create series
   const series = chart.series.push(new am4charts.LineSeries());
   series.dataFields.valueY = "values";
   series.dataFields.dateX = "labels";
   series.tooltipText = "{valueY}";
   series.strokeWidth = 2;
+  if (series.tooltip) {
+    series.tooltip.pointerOrientation = "vertical";
+  }
+
   // Make bullets grow on hover
   const bullet = series.bullets.push(new am4charts.CircleBullet());
   bullet.circle.strokeWidth = 2;
   bullet.circle.radius = 4;
   bullet.circle.fill = am4core.color("#fff");
+
   const bullethover = bullet.states.create("hover");
   bullethover.properties.scale = 1.3;
-  series.tooltip.pointerOrientation = "vertical";
+
   chart.cursor = new am4charts.XYCursor();
   chart.cursor.snapToSeries = series;
   chart.cursor.xAxis = dateAxis;
@@ -82,15 +96,18 @@ const drawBarChart = (chartDivId: string, dataUrl: string | null): void => {
   chart.exporting.menu = new am4core.ExportMenu();
   chart.exporting.menu.align = "right";
   chart.exporting.menu.verticalAlign = "bottom";
+
   // Set theme
   am4core.useTheme(am4themes_material);
   chart.dataSource.url = dataUrl || "";
   chart.dataSource.parser.options.emptyAs = 0;
+
   const categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
   categoryAxis.dataFields.category = "labels";
   categoryAxis.renderer.grid.template.location = 0;
   categoryAxis.renderer.minGridDistance = 30;
   chart.yAxes.push(new am4charts.ValueAxis());
+
   // Create series
   const series = chart.series.push(new am4charts.ColumnSeries());
   series.dataFields.valueY = "values";
@@ -98,6 +115,7 @@ const drawBarChart = (chartDivId: string, dataUrl: string | null): void => {
   series.name = "Hits";
   series.columns.template.tooltipText = "{valueY} hits";
   series.columns.template.fillOpacity = 0.8;
+
   const columnTemplate = series.columns.template;
   columnTemplate.strokeWidth = 2;
   columnTemplate.strokeOpacity = 1;
@@ -113,73 +131,47 @@ const drawXYDurationChart = (
   chart.exporting.menu = new am4core.ExportMenu();
   chart.exporting.menu.align = "right";
   chart.exporting.menu.verticalAlign = "bottom";
+
   // Set theme
   am4core.useTheme(am4themes_material);
+
   // Create axes
   const dateAxis = chart.xAxes.push(new am4charts.DateAxis());
   dateAxis.renderer.minGridDistance = 60;
+
   const yAxis = chart.yAxes.push(new am4charts.DurationAxis());
   yAxis.baseUnit = "second";
   chart.durationFormatter.durationFormat = "hh'h,' mm'mn'";
+
   // Add data
   chart.dataSource.url = dataUrl || "";
   chart.dataSource.parser.options.emptyAs = 0;
+
   // Create series
   const series = chart.series.push(new am4charts.LineSeries());
   series.dataFields.valueY = "values";
   series.dataFields.dateX = "labels";
   series.tooltipText = "{valueY.formatDuration()}";
   series.strokeWidth = 2;
+  if (series.tooltip) {
+    series.tooltip.pointerOrientation = "vertical";
+  }
+
   // Make bullets grow on hover
   const bullet = series.bullets.push(new am4charts.CircleBullet());
   bullet.circle.strokeWidth = 2;
   bullet.circle.radius = 4;
   bullet.circle.fill = am4core.color("#fff");
+
   const bullethover = bullet.states.create("hover");
   bullethover.properties.scale = 1.3;
-  series.tooltip.pointerOrientation = "vertical";
+
   chart.cursor = new am4charts.XYCursor();
   chart.cursor.snapToSeries = series;
   chart.cursor.xAxis = dateAxis;
   chart.scrollbarX = new am4core.Scrollbar();
 };
 
-const drawXYSeriesChart = (
-  chartDivId: string,
-  dataUrl: string | null
-): void => {
-  // Create chart instance
-  const chart = am4core.create(chartDivId, am4charts.XYChart);
-  am4core.percent(100);
-  chart.exporting.menu = new am4core.ExportMenu();
-  chart.exporting.menu.align = "right";
-  chart.exporting.menu.verticalAlign = "bottom";
-  // Set theme
-  am4core.useTheme(am4themes_material);
-  // Create axes
-  chart.xAxes.push(new am4charts.ValueAxis());
-  chart.yAxes.push(new am4charts.ValueAxis());
-  // Add data
-  chart.dataSource.url = dataUrl || "";
-  chart.dataSource.parser.options.emptyAs = 0;
-  // Create series
-  const series1 = chart.series.push(new am4charts.LineSeries());
-  series1.dataFields.valueX = "X";
-  series1.dataFields.valueY = "aY";
-  const series2 = chart.series.push(new am4charts.LineSeries());
-  series2.dataFields.valueX = "X";
-  series2.dataFields.valueY = "bY";
-  const series3 = chart.series.push(new am4charts.LineSeries());
-  series3.dataFields.valueX = "X";
-  series3.dataFields.valueY = "cY";
-  const series4 = chart.series.push(new am4charts.LineSeries());
-  series4.dataFields.valueX = "X";
-  series4.dataFields.valueY = "dY";
-  const series5 = chart.series.push(new am4charts.LineSeries());
-  series5.dataFields.valueX = "X";
-  series5.dataFields.valueY = "eY";
-};
-
 const drawMapChart = (chartDivId: string, dataUrl: string | null): void => {
   // Create map instance
   const chart = am4core.create(chartDivId, am4maps.MapChart);
@@ -187,25 +179,34 @@ const drawMapChart = (chartDivId: string, dataUrl: string | null): void => {
   chart.exporting.menu = new am4core.ExportMenu();
   chart.exporting.menu.align = "left";
   chart.exporting.menu.verticalAlign = "top";
+
   // Set theme
   am4core.useTheme(am4themes_material);
+
   // Set map definition
   chart.geodata = am4geodata_worldLow;
+
   // Set projection
   chart.projection = new am4maps.projections.Miller();
+
   // Create map polygon series
   const polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
+
   // Exclude Antartica
   polygonSeries.exclude = ["AQ"];
+
   // Make map load polygon (like country names) data from GeoJSON
   polygonSeries.useGeodata = true;
+
   // Configure series
   const polygonTemplate = polygonSeries.mapPolygons.template;
   polygonTemplate.tooltipText = "{name}";
   polygonTemplate.polygon.fillOpacity = 0.6;
+
   // Create hover state and set alternative fill color
   const hs = polygonTemplate.states.create("hover");
   hs.properties.fill = chart.colors.getIndex(0);
+
   // Add image series
   const imageSeries = chart.series.push(new am4maps.MapImageSeries());
   imageSeries.dataSource.url = dataUrl || "";
@@ -213,9 +214,11 @@ const drawMapChart = (chartDivId: string, dataUrl: string | null): void => {
   imageSeries.mapImages.template.propertyFields.latitude = "latitude";
   imageSeries.mapImages.template.tooltipText =
     "{country_code}, {region_code}:\n[bold]{value}[/] hits";
+
   const circle = imageSeries.mapImages.template.createChild(am4core.Circle);
   circle.radius = 1;
   circle.fill = am4core.color("#60f");
+
   imageSeries.heatRules.push({
     target: circle,
     property: "radius",
@@ -233,6 +236,7 @@ const DrawCharts = (): void => {
   for (let i = 0; i < chartDivs.length; i++) {
     const chartDiv: HTMLDivElement = chartDivs[i];
     const chartType = chartDiv.dataset.chartType;
+
     switch (chartType) {
       case "pie-chart":
         drawPieChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
@@ -249,9 +253,6 @@ const DrawCharts = (): void => {
           chartDiv.getAttribute("data-chart-url")
         );
         break;
-      case "xy-series-chart":
-        drawXYSeriesChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
-        break;
       case "map-chart":
         drawMapChart(chartDiv.id, chartDiv.getAttribute("data-chart-url"));
         break;
diff --git a/app/Views/_assets/modules/Soundbites.ts b/app/Views/_assets/modules/Soundbites.ts
index 601b2b7020..7c2009fa0f 100644
--- a/app/Views/_assets/modules/Soundbites.ts
+++ b/app/Views/_assets/modules/Soundbites.ts
@@ -1,3 +1,6 @@
+/**
+ * TODO: refactor file
+ */
 let timeout: number | null = null;
 
 const playSoundbite = (
diff --git a/app/Views/admin/_partials/_user_info.php b/app/Views/admin/_partials/_user_info.php
index e116b04e5e..2b2d2eda2e 100644
--- a/app/Views/admin/_partials/_user_info.php
+++ b/app/Views/admin/_partials/_user_info.php
@@ -29,4 +29,4 @@
     <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
     [<?= implode(', ', $user->permissions) ?>]
     </dd>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/Views/admin/contributor/add.php b/app/Views/admin/contributor/add.php
index 8d8530d837..4bd2381c3f 100644
--- a/app/Views/admin/contributor/add.php
+++ b/app/Views/admin/contributor/add.php
@@ -32,9 +32,9 @@
 
 <?= button(
     lang('Contributor.form.submit_add'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/contributor/edit.php b/app/Views/admin/contributor/edit.php
index cc995aeecf..33398d9941 100644
--- a/app/Views/admin/contributor/edit.php
+++ b/app/Views/admin/contributor/edit.php
@@ -25,9 +25,9 @@
 
 <?= button(
     lang('Contributor.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/contributor/list.php b/app/Views/admin/contributor/list.php
index e2b3178e18..f83064d9c8 100644
--- a/app/Views/admin/contributor/list.php
+++ b/app/Views/admin/contributor/list.php
@@ -40,29 +40,32 @@
                     route_to(
                         'contributor-edit',
                         $podcast->id,
-                        $contributor->id
+                        $contributor->id,
                     ),
                     [
                         'variant' => 'info',
                         'size' => 'small',
                     ],
-                    ['class' => 'mr-2']
+                    ['class' => 'mr-2'],
                 ) .
                     button(
                         lang('Contributor.remove'),
                         route_to(
                             'contributor-remove',
                             $podcast->id,
-                            $contributor->id
+                            $contributor->id,
                         ),
-                        ['variant' => 'danger', 'size' => 'small'],
-                        ['class' => 'mr-2']
+                        [
+                            'variant' => 'danger',
+                            'size' => 'small',
+                        ],
+                        ['class' => 'mr-2'],
                     );
             },
         ],
     ],
     $podcast->contributors,
-    $podcast
+    $podcast,
 ) ?>
 
 <?= $this->endSection() ?>
diff --git a/app/Views/admin/episode/create.php b/app/Views/admin/episode/create.php
index 883af9f72c..77501a2017 100644
--- a/app/Views/admin/episode/create.php
+++ b/app/Views/admin/episode/create.php
@@ -131,7 +131,7 @@
     <?= form_radio(
         ['id' => 'trailer', 'name' => 'type', 'class' => 'form-radio-btn'],
         'trailer',
-        old('type') ? old('type') == 'trailer' : false,
+        old('type') && old('type') == 'trailer',
     ) ?>
     <label for="trailer" class="inline-flex items-center">
         <?= lang('Episode.form.type.trailer') ?>
@@ -139,7 +139,7 @@
     <?= form_radio(
         ['id' => 'bonus', 'name' => 'type', 'class' => 'form-radio-btn'],
         'bonus',
-        old('type') ? old('type') == 'bonus' : false,
+        old('type') && old('type') == 'bonus',
     ) ?>
     <label for="bonus" class="inline-flex items-center">
         <?= lang('Episode.form.type.bonus') ?>
@@ -172,7 +172,7 @@
             'class' => 'form-radio-btn',
         ],
         'clean',
-        old('parental_advisory') ? old('parental_advisory') === 'clean' : false,
+        old('parental_advisory') && old('parental_advisory') === 'clean',
     ) ?>
     <label for="clean"><?= lang(
         'Episode.form.parental_advisory.clean',
@@ -184,9 +184,7 @@
             'class' => 'form-radio-btn',
         ],
         'explicit',
-        old('parental_advisory')
-            ? old('parental_advisory') === 'explicit'
-            : false,
+        old('parental_advisory') && old('parental_advisory') === 'explicit',
     ) ?>
     <label for="explicit"><?= lang(
         'Episode.form.parental_advisory.explicit',
@@ -421,7 +419,7 @@
 
 <?= button(
     lang('Episode.form.submit_create'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/episode/edit.php b/app/Views/admin/episode/edit.php
index 1965b0c68c..d394982cc3 100644
--- a/app/Views/admin/episode/edit.php
+++ b/app/Views/admin/episode/edit.php
@@ -60,7 +60,6 @@
 <?= form_input([
     'id' => 'image',
     'name' => 'image',
-
     'class' => 'form-input',
     'type' => 'file',
     'accept' => '.jpg,.jpeg,.png',
@@ -284,9 +283,9 @@
         ')</small>' .
         hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?></legend>
     <div class="mb-4 form-input-tabs">
-        <input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= !$episode->transcript_file_remote_url
-            ? 'checked'
-            : '' ?> />
+        <input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= $episode->transcript_file_remote_url
+            ? ''
+            : 'checked' ?> />
         <label for="transcript-file-upload-choice"><?= lang(
             'Common.forms.upload_file',
         ) ?></label>
@@ -377,9 +376,9 @@
         ')</small>' .
         hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?></legend>
     <div class="mb-4 form-input-tabs">
-        <input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= !$episode->chapters_file_remote_url
-            ? 'checked'
-            : '' ?> />
+        <input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= $episode->chapters_file_remote_url
+            ? ''
+            : 'checked' ?> />
         <label for="chapters-file-upload-choice"><?= lang(
             'Common.forms.upload_file',
         ) ?></label>
@@ -493,7 +492,7 @@
 
 <?= button(
     lang('Episode.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/episode/embeddable_player.php b/app/Views/admin/episode/embeddable_player.php
index ea5c409bbc..ebb99e24bf 100644
--- a/app/Views/admin/episode/embeddable_player.php
+++ b/app/Views/admin/episode/embeddable_player.php
@@ -38,7 +38,7 @@
     <?= icon_button(
         'file-copy',
         lang('Episode.embeddable_player.clipboard_iframe'),
-        null,
+        '',
         ['variant' => 'default'],
         ['data-type' => 'clipboard-copy', 'data-clipboard-target' => 'iframe'],
     ) ?>
@@ -56,7 +56,7 @@
     <?= icon_button(
         'file-copy',
         lang('Episode.embeddable_player.clipboard_url'),
-        null,
+        '',
         ['variant' => 'default'],
         ['data-type' => 'clipboard-copy', 'data-clipboard-target' => 'url'],
     ) ?>
diff --git a/app/Views/admin/episode/person.php b/app/Views/admin/episode/person.php
index be803b50f4..03a828f172 100644
--- a/app/Views/admin/episode/person.php
+++ b/app/Views/admin/episode/person.php
@@ -13,7 +13,7 @@
     lang('Person.create'),
     route_to('person-create'),
     ['variant' => 'primary', 'iconLeft' => 'add'],
-    ['class' => 'mr-2']
+    ['class' => 'mr-2'],
 ) ?>
 <?= $this->endSection() ?>
 
@@ -29,7 +29,7 @@
 
 <?= form_section(
     lang('Person.episode_form.manage_section_title'),
-    lang('Person.episode_form.manage_section_subtitle')
+    lang('Person.episode_form.manage_section_subtitle'),
 ) ?>
 
 
@@ -47,11 +47,11 @@
                     ($episodePerson->person_group && $episodePerson->person_role
                         ? '<span class="text-sm text-gray-600">' .
                             lang(
-                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.label"
+                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.label",
                             ) .
                             ' â–¸ ' .
                             lang(
-                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label"
+                                "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label",
                             ) .
                             '</span>'
                         : '') .
@@ -65,21 +65,24 @@
         ],
         [
             'header' => lang('Common.actions'),
-            'cell' => function ($episodePerson) {
+            'cell' => function ($episodePerson): string {
                 return button(
                     lang('Person.episode_form.remove'),
                     route_to(
                         'episode-person-remove',
                         $episodePerson->podcast_id,
                         $episodePerson->episode_id,
-                        $episodePerson->id
+                        $episodePerson->id,
                     ),
-                    ['variant' => 'danger', 'size' => 'small']
+                    [
+                        'variant' => 'danger',
+                        'size' => 'small',
+                    ],
                 );
             },
         ],
     ],
-    $episodePersons
+    $episodePersons,
 ) ?>
 
 <?= form_section_close() ?>
@@ -88,14 +91,14 @@
 
 <?= form_section(
     lang('Person.episode_form.add_section_title'),
-    lang('Person.episode_form.add_section_subtitle')
+    lang('Person.episode_form.add_section_subtitle'),
 ) ?>
 
 <?= form_label(
     lang('Person.episode_form.person'),
     'person',
     [],
-    lang('Person.episode_form.person_hint')
+    lang('Person.episode_form.person_hint'),
 ) ?>
 <?= form_multiselect('person[]', $personOptions, old('person', []), [
     'id' => 'person',
@@ -107,24 +110,26 @@
     lang('Person.episode_form.group_role'),
     'group_role',
     [],
-
     lang('Person.episode_form.group_role_hint'),
-    true
+    true,
 ) ?>
 <?= form_multiselect(
     'person_group_role[]',
     $taxonomyOptions,
     old('person_group_role', []),
-    ['id' => 'person_group_role', 'class' => 'form-select mb-4']
+    [
+        'id' => 'person_group_role',
+        'class' => 'form-select mb-4',
+    ],
 ) ?>
         
     
 <?= form_section_close() ?>
 <?= button(
     lang('Person.episode_form.submit_add'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?> 
 <?= form_close() ?>
 
diff --git a/app/Views/admin/episode/publish.php b/app/Views/admin/episode/publish.php
index 4cc1d7c71a..d6664bbe1a 100644
--- a/app/Views/admin/episode/publish.php
+++ b/app/Views/admin/episode/publish.php
@@ -117,9 +117,8 @@
                 'class' => 'text-pine-700',
             ],
             'schedule',
-            old('publication_method')
-                ? old('publication_method') === 'schedule'
-                : false,
+            old('publication_method') &&
+                old('publication_method') === 'schedule',
         ) ?>
         <label for="schedule" class="ml-2"><?= lang(
             'Episode.publish_form.publication_method.schedule',
@@ -160,7 +159,7 @@
 
 <?= button(
     lang('Episode.publish_form.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit'],
 ) ?>
diff --git a/app/Views/admin/episode/publish_edit.php b/app/Views/admin/episode/publish_edit.php
index 973dc5d546..e98959f102 100644
--- a/app/Views/admin/episode/publish_edit.php
+++ b/app/Views/admin/episode/publish_edit.php
@@ -117,7 +117,7 @@
             'class' => 'text-pine-700',
         ],
         'now',
-        old('publication_method') ? old('publish') === 'now' : false,
+        old('publication_method') && old('publish') === 'now',
     ) ?>
     <span class="ml-2"><?= lang(
         'Episode.publish_form.publication_method.now',
@@ -175,7 +175,7 @@
 
     <?= button(
         lang('Episode.publish_form.submit_edit'),
-        null,
+        '',
         ['variant' => 'primary'],
         ['type' => 'submit'],
     ) ?>
diff --git a/app/Views/admin/episode/soundbites.php b/app/Views/admin/episode/soundbites.php
index e4893e77a6..e8c7579607 100644
--- a/app/Views/admin/episode/soundbites.php
+++ b/app/Views/admin/episode/soundbites.php
@@ -88,7 +88,7 @@
             <td class="px-4 py-2"><?= icon_button(
                 'play',
                 lang('Episode.soundbites_form.play'),
-                null,
+                '',
                 ['variant' => 'primary'],
                 [
                     'class' => 'mb-1 mr-1',
@@ -187,7 +187,7 @@
 
 <?= button(
     lang('Episode.soundbites_form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/episode/unpublish.php b/app/Views/admin/episode/unpublish.php
index 829808136e..64f1c465bb 100644
--- a/app/Views/admin/episode/unpublish.php
+++ b/app/Views/admin/episode/unpublish.php
@@ -41,7 +41,7 @@
 
 <?= button(
     lang('Episode.unpublish_form.submit'),
-    null,
+    '',
     ['variant' => 'danger'],
     ['type' => 'submit'],
 ) ?>
diff --git a/app/Views/admin/episode/view.php b/app/Views/admin/episode/view.php
index c5a0a88323..2f2ca3a7ad 100644
--- a/app/Views/admin/episode/view.php
+++ b/app/Views/admin/episode/view.php
@@ -21,10 +21,6 @@
     $podcast->id,
     $episode->id,
     $episode->publication_status,
-    lang('Episode.publish'),
-    route_to('episode-publish', $podcast->id, $episode->id),
-    ['variant' => 'accent', 'iconLeft' => 'upload'],
-    ['class' => 'mr-2'],
 ) ?>
 <?= $this->endSection() ?>
 
@@ -97,11 +93,11 @@
         [
             [
                 'header' => 'Play',
-                'cell' => function ($soundbite) {
+                'cell' => function ($soundbite): string {
                     return icon_button(
                         'play',
                         lang('Episode.soundbites_form.play'),
-                        null,
+                        '',
                         ['variant' => 'primary'],
                         [
                             'class' => 'mb-1 mr-1',
@@ -115,13 +111,13 @@
             ],
             [
                 'header' => lang('Episode.soundbites_form.start_time'),
-                'cell' => function ($soundbite) {
+                'cell' => function ($soundbite): string {
                     return format_duration($soundbite->start_time);
                 },
             ],
             [
                 'header' => lang('Episode.soundbites_form.duration'),
-                'cell' => function ($soundbite) {
+                'cell' => function ($soundbite): string {
                     return format_duration($soundbite->duration);
                 },
             ],
diff --git a/app/Views/admin/fediverse/blocked_actors.php b/app/Views/admin/fediverse/blocked_actors.php
index 40505748e7..0cc8ba5aad 100644
--- a/app/Views/admin/fediverse/blocked_actors.php
+++ b/app/Views/admin/fediverse/blocked_actors.php
@@ -34,7 +34,7 @@
 
 <?= button(
     lang('Fediverse.block_lists_form.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/fediverse/blocked_domains.php b/app/Views/admin/fediverse/blocked_domains.php
index c6a440ddd2..713b737b31 100644
--- a/app/Views/admin/fediverse/blocked_domains.php
+++ b/app/Views/admin/fediverse/blocked_domains.php
@@ -33,7 +33,7 @@
 
 <?= button(
     lang('Fediverse.block_lists_form.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/my_account/change_password.php b/app/Views/admin/my_account/change_password.php
index 0ebf49946d..6af3e6d5f9 100644
--- a/app/Views/admin/my_account/change_password.php
+++ b/app/Views/admin/my_account/change_password.php
@@ -32,14 +32,15 @@
     'class' => 'form-input mb-4',
     'required' => 'required',
     'type' => 'password',
+
     'autocomplete' => 'new-password',
 ]) ?>
 
 <?= button(
     lang('User.form.submit_password_change'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/page/create.php b/app/Views/admin/page/create.php
index 1147f9d9c2..94b009164b 100644
--- a/app/Views/admin/page/create.php
+++ b/app/Views/admin/page/create.php
@@ -46,16 +46,16 @@
             'required' => 'required',
         ],
         old('content', '', false),
-        'data-editor="markdown"'
+        'data-editor="markdown"',
     ) ?>
 </div>
 
 
 <?= button(
     lang('Page.form.submit_create'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/page/edit.php b/app/Views/admin/page/edit.php
index 9d23cddff3..e12bbaad01 100644
--- a/app/Views/admin/page/edit.php
+++ b/app/Views/admin/page/edit.php
@@ -46,15 +46,15 @@
             'required' => 'required',
         ],
         old('content', $page->content, false),
-        'data-editor="markdown"'
+        'data-editor="markdown"',
     ) ?>
 </div>
 
 <?= button(
     lang('Page.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/page/list.php b/app/Views/admin/page/list.php
index 745ddc5ef8..2a113a21f1 100644
--- a/app/Views/admin/page/list.php
+++ b/app/Views/admin/page/list.php
@@ -40,23 +40,23 @@
                         'variant' => 'secondary',
                         'size' => 'small',
                     ],
-                    ['class' => 'mr-2']
+                    ['class' => 'mr-2'],
                 ) .
                     button(
                         lang('Page.edit'),
                         route_to('page-edit', $page->id),
                         ['variant' => 'info', 'size' => 'small'],
-                        ['class' => 'mr-2']
+                        ['class' => 'mr-2'],
                     ) .
                     button(
                         lang('Page.delete'),
                         route_to('page-delete', $page->id),
-                        ['variant' => 'danger', 'size' => 'small']
+                        ['variant' => 'danger', 'size' => 'small'],
                     );
             },
         ],
     ],
-    $pages
+    $pages,
 ) ?>
 
 <?= $this->endSection() ?>
diff --git a/app/Views/admin/person/create.php b/app/Views/admin/person/create.php
index 5ec2c7ee5c..b89ae18816 100644
--- a/app/Views/admin/person/create.php
+++ b/app/Views/admin/person/create.php
@@ -19,14 +19,14 @@
 
 <?= form_section(
     lang('Person.form.identity_section_title'),
-    lang('Person.form.identity_section_subtitle')
+    lang('Person.form.identity_section_subtitle'),
 ) ?>
 
 <?= form_label(
     lang('Person.form.full_name'),
     'full_name',
     [],
-    lang('Person.form.full_name_hint')
+    lang('Person.form.full_name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'full_name',
@@ -41,7 +41,7 @@
     lang('Person.form.unique_name'),
     'unique_name',
     [],
-    lang('Person.form.unique_name_hint')
+    lang('Person.form.unique_name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'unique_name',
@@ -57,7 +57,7 @@
     'information_url',
     [],
     lang('Person.form.information_url_hint'),
-    true
+    true,
 ) ?>
 <?= form_input([
     'id' => 'information_url',
@@ -76,16 +76,16 @@
     'accept' => '.jpg,.jpeg,.png',
 ]) ?>
 <small class="mb-4 text-gray-600"><?= lang(
-    'Person.form.image_size_hint'
+    'Person.form.image_size_hint',
 ) ?></small>
 
 <?= form_section_close() ?>
 
 <?= button(
     lang('Person.form.submit_create'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 
diff --git a/app/Views/admin/person/edit.php b/app/Views/admin/person/edit.php
index 98a1d629b9..c76f3c541a 100644
--- a/app/Views/admin/person/edit.php
+++ b/app/Views/admin/person/edit.php
@@ -20,14 +20,14 @@
 <?= form_section(
     lang('Person.form.identity_section_title'),
     lang('Person.form.identity_section_subtitle') .
-        "<img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />"
+        "<img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />",
 ) ?>
 
 <?= form_label(
     lang('Person.form.full_name'),
     'full_name',
     [],
-    lang('Person.form.full_name_hint')
+    lang('Person.form.full_name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'full_name',
@@ -42,7 +42,7 @@
     lang('Person.form.unique_name'),
     'unique_name',
     [],
-    lang('Person.form.unique_name_hint')
+    lang('Person.form.unique_name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'unique_name',
@@ -58,7 +58,7 @@
     'information_url',
     [],
     lang('Person.form.information_url_hint'),
-    true
+    true,
 ) ?>
 <?= form_input([
     'id' => 'information_url',
@@ -76,16 +76,16 @@
     'accept' => '.jpg,.jpeg,.png',
 ]) ?>
 <small class="mb-4 text-gray-600"><?= lang(
-    'Person.form.image_size_hint'
+    'Person.form.image_size_hint',
 ) ?></small>
 
 <?= form_section_close() ?>
 
 <?= button(
     lang('Person.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 
diff --git a/app/Views/admin/person/list.php b/app/Views/admin/person/list.php
index de4040fd7a..193eb7e875 100644
--- a/app/Views/admin/person/list.php
+++ b/app/Views/admin/person/list.php
@@ -13,7 +13,7 @@
     lang('Person.create'),
     route_to('person-create'),
     ['variant' => 'primary', 'iconLeft' => 'add'],
-    ['class' => 'mr-2']
+    ['class' => 'mr-2'],
 ) ?>
 <?= $this->endSection() ?>
 
@@ -26,11 +26,11 @@
             <img
             alt="<?= $person->full_name ?>"
             src="<?= $person->image
-                ->thumbnail_url ?>" class="object-cover w-40 w-full" />
+                ->thumbnail_url ?>" class="object-cover w-full" />
             <div class="p-2">
                 <a href="<?= route_to(
                     'person-view',
-                    $person->id
+                    $person->id,
                 ) ?>" class="hover:underline">
                     <h2 class="font-semibold"><?= $person->full_name ?></h2>
                 </a>
@@ -38,21 +38,21 @@
             <footer class="flex items-center justify-end p-2">
                 <a class="inline-flex p-2 mr-2 text-teal-700 bg-teal-100 rounded-full shadow-xs hover:bg-teal-200" href="<?= route_to(
                     'person-edit',
-                    $person->id
+                    $person->id,
                 ) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
-    'Person.edit'
+    'Person.edit',
 ) ?>"><?= icon('edit') ?></a>
                 <a class="inline-flex p-2 mr-2 text-gray-700 bg-red-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
                     'person-delete',
-                    $person->id
+                    $person->id,
                 ) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
-    'Person.delete'
+    'Person.delete',
 ) ?>"><?= icon('delete-bin') ?></a>
                 <a class="inline-flex p-2 text-gray-700 bg-gray-100 rounded-full shadow-xs hover:bg-gray-200" href="<?= route_to(
                     'person-view',
-                    $person->id
+                    $person->id,
                 ) ?>" data-toggle="tooltip" data-placement="bottom" title="<?= lang(
-    'Person.view'
+    'Person.view',
 ) ?>"><?= icon('eye') ?></a>
             </footer>
         </article>
diff --git a/app/Views/admin/person/view.php b/app/Views/admin/person/view.php
index 99446eb41e..2191606417 100644
--- a/app/Views/admin/person/view.php
+++ b/app/Views/admin/person/view.php
@@ -14,7 +14,7 @@
     lang('Person.edit'),
     route_to('person-edit', $person->id),
     ['variant' => 'secondary', 'iconLeft' => 'edit'],
-    ['class' => 'mr-2']
+    ['class' => 'mr-2'],
 ) ?>
 <?= $this->endSection() ?>
 
diff --git a/app/Views/admin/podcast/analytics/listening_time.php b/app/Views/admin/podcast/analytics/listening_time.php
index 2285f3761c..d7c1a5578e 100644
--- a/app/Views/admin/podcast/analytics/listening_time.php
+++ b/app/Views/admin/podcast/analytics/listening_time.php
@@ -16,7 +16,7 @@
     'analytics-data',
     $podcast->id,
     'Podcast',
-    'TotalListeningTimeByDay'
+    'TotalListeningTimeByDay',
 ) ?>"></div>
 </div>
 
@@ -26,7 +26,7 @@
     'analytics-data',
     $podcast->id,
     'Podcast',
-    'TotalListeningTimeByMonth'
+    'TotalListeningTimeByMonth',
 ) ?>"></div>
 </div>
 
diff --git a/app/Views/admin/podcast/analytics/locations.php b/app/Views/admin/podcast/analytics/locations.php
index 3504529bc2..b2bbb83223 100644
--- a/app/Views/admin/podcast/analytics/locations.php
+++ b/app/Views/admin/podcast/analytics/locations.php
@@ -17,7 +17,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByCountry',
-            'Weekly'
+            'Weekly',
         ) ?>"></div>
     </div>
 
@@ -27,7 +27,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByCountry',
-            'Yearly'
+            'Yearly',
         ) ?>"></div>
     </div>
 </div>
@@ -37,7 +37,7 @@
 <div class="chart-map" id="by-region-map" data-chart-type="map-chart" data-chart-url="<?= route_to(
     'analytics-full-data',
     $podcast->id,
-    'PodcastByRegion'
+    'PodcastByRegion',
 ) ?>"></div>
 </div>
 
diff --git a/app/Views/admin/podcast/analytics/players.php b/app/Views/admin/podcast/analytics/players.php
index 667918a178..a30e9fee70 100644
--- a/app/Views/admin/podcast/analytics/players.php
+++ b/app/Views/admin/podcast/analytics/players.php
@@ -17,7 +17,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByPlayer',
-            'ByAppWeekly'
+            'ByAppWeekly',
         ) ?>"></div>
     </div>
 
@@ -27,7 +27,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByService',
-            'ByServiceWeekly'
+            'ByServiceWeekly',
         ) ?>"></div>
     </div>
 
@@ -37,7 +37,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByPlayer',
-            'ByDeviceWeekly'
+            'ByDeviceWeekly',
         ) ?>"></div>
     </div>
 
@@ -47,7 +47,7 @@
             'analytics-data',
             $podcast->id,
             'PodcastByPlayer',
-            'ByOsWeekly'
+            'ByOsWeekly',
         ) ?>"></div>
     </div>
 </div>
@@ -58,7 +58,7 @@
         'analytics-data',
         $podcast->id,
         'PodcastByPlayer',
-        'Bots'
+        'Bots',
     ) ?>"></div>
 </div>
 
diff --git a/app/Views/admin/podcast/analytics/time_periods.php b/app/Views/admin/podcast/analytics/time_periods.php
index 8b56873072..66264a03a2 100644
--- a/app/Views/admin/podcast/analytics/time_periods.php
+++ b/app/Views/admin/podcast/analytics/time_periods.php
@@ -17,7 +17,7 @@
             'analytics-data',
             $podcast->id,
             'Podcast',
-            'ByWeekday'
+            'ByWeekday',
         ) ?>"></div>
     </div>
 
@@ -26,7 +26,7 @@
         <div class="chart-xy" id="by-hour-barchart" data-chart-type="bar-chart" data-chart-url="<?= route_to(
             'analytics-full-data',
             $podcast->id,
-            'PodcastByHour'
+            'PodcastByHour',
         ) ?>"></div>
     </div>
 
diff --git a/app/Views/admin/podcast/analytics/unique_listeners.php b/app/Views/admin/podcast/analytics/unique_listeners.php
index 8803a54a73..698c33e2a4 100644
--- a/app/Views/admin/podcast/analytics/unique_listeners.php
+++ b/app/Views/admin/podcast/analytics/unique_listeners.php
@@ -16,7 +16,7 @@
     'analytics-data',
     $podcast->id,
     'Podcast',
-    'UniqueListenersByDay'
+    'UniqueListenersByDay',
 ) ?>"></div>
 </div>
 
@@ -26,7 +26,7 @@
     'analytics-data',
     $podcast->id,
     'Podcast',
-    'UniqueListenersByMonth'
+    'UniqueListenersByMonth',
 ) ?>"></div>
 </div>
 
diff --git a/app/Views/admin/podcast/analytics/webpages.php b/app/Views/admin/podcast/analytics/webpages.php
index 5ec0f99a8b..f6c79001f9 100644
--- a/app/Views/admin/podcast/analytics/webpages.php
+++ b/app/Views/admin/podcast/analytics/webpages.php
@@ -18,7 +18,7 @@
             'analytics-data',
             $podcast->id,
             'WebsiteByReferer',
-            'ByDomainWeekly'
+            'ByDomainWeekly',
         ) ?>"></div>
     </div>
 
@@ -29,7 +29,7 @@
             'analytics-data',
             $podcast->id,
             'WebsiteByReferer',
-            'ByDomainYearly'
+            'ByDomainYearly',
         ) ?>"></div>
     </div>
 
@@ -39,7 +39,7 @@
         <div class="chart-pie" id="by-entry-page-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
             'analytics-full-data',
             $podcast->id,
-            'WebsiteByEntryPage'
+            'WebsiteByEntryPage',
         ) ?>"></div>
     </div>
 
@@ -49,7 +49,7 @@
         <div class="chart-pie" id="by-browser-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
             'analytics-full-data',
             $podcast->id,
-            'WebsiteByBrowser'
+            'WebsiteByBrowser',
         ) ?>"></div>
     </div>
     
diff --git a/app/Views/admin/podcast/create.php b/app/Views/admin/podcast/create.php
index b30e6abf8d..1686104e7a 100644
--- a/app/Views/admin/podcast/create.php
+++ b/app/Views/admin/podcast/create.php
@@ -19,7 +19,7 @@
 
 <?= form_section(
     lang('Podcast.form.identity_section_title'),
-    lang('Podcast.form.identity_section_subtitle')
+    lang('Podcast.form.identity_section_subtitle'),
 ) ?>
 
 <?= form_label(lang('Podcast.form.image'), 'image') ?>
@@ -32,7 +32,7 @@
     'accept' => '.jpg,.jpeg,.png',
 ]) ?>
 <small class="mb-4 text-gray-600"><?= lang(
-    'Common.forms.image_size_hint'
+    'Common.forms.image_size_hint',
 ) ?></small>
 
 <?= form_label(lang('Podcast.form.title'), 'title') ?>
@@ -48,7 +48,7 @@
     lang('Podcast.form.name'),
     'name',
     [],
-    lang('Podcast.form.name_hint')
+    lang('Podcast.form.name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'name',
@@ -58,9 +58,7 @@
     'required' => 'required',
 ]) ?>
 
-<?= form_fieldset('', [
-    'class' => 'mb-4',
-]) ?>
+<?= form_fieldset('', ['class' => 'mb-4']) ?>
     <legend>
     <?= lang('Podcast.form.type.label') .
         hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
@@ -68,13 +66,13 @@
     <?= form_radio(
         ['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'],
         'episodic',
-        old('type') ? old('type') == 'episodic' : true
+        old('type') ? old('type') == 'episodic' : true,
     ) ?>
     <label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
     <?= form_radio(
         ['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'],
         'serial',
-        old('type') ? old('type') == 'serial' : false
+        old('type') && old('type') == 'serial',
     ) ?>
     <label for="serial"><?= lang('Podcast.form.type.serial') ?></label>
 <?= form_fieldset_close() ?>
@@ -89,7 +87,7 @@
             'required' => 'required',
         ],
         old('description', '', false),
-        'data-editor="markdown"'
+        'data-editor="markdown"',
     ) ?>
 </div>
 
@@ -98,7 +96,7 @@
 
 <?= form_section(
     lang('Podcast.form.classification_section_title'),
-    lang('Podcast.form.classification_section_subtitle')
+    lang('Podcast.form.classification_section_subtitle'),
 ) ?>
 
 <?= form_label(lang('Podcast.form.language'), 'language') ?>
@@ -120,7 +118,7 @@
     'other_categories',
     [],
     '',
-    true
+    true,
 ) ?>
 <?= form_multiselect(
     'other_categories[]',
@@ -130,7 +128,7 @@
         'id' => 'other_categories',
         'class' => 'mb-4',
         'data-max-item-count' => '2',
-    ]
+    ],
 ) ?>
 
 <?= form_fieldset('', ['class' => 'mb-4']) ?>
@@ -147,10 +145,10 @@
         'undefined',
         old('parental_advisory')
             ? old('parental_advisory') === 'undefined'
-            : true
+            : true,
     ) ?>
     <label for="undefined"><?= lang(
-        'Podcast.form.parental_advisory.undefined'
+        'Podcast.form.parental_advisory.undefined',
     ) ?></label>
     <?= form_radio(
         [
@@ -159,10 +157,10 @@
             'class' => 'form-radio-btn',
         ],
         'clean',
-        old('parental_advisory') ? old('parental_advisory') === 'clean' : false
+        old('parental_advisory') && old('parental_advisory') === 'clean',
     ) ?>
     <label for="clean"><?= lang(
-        'Podcast.form.parental_advisory.clean'
+        'Podcast.form.parental_advisory.clean',
     ) ?></label>
     <?= form_radio(
         [
@@ -171,12 +169,10 @@
             'class' => 'form-radio-btn',
         ],
         'explicit',
-        old('parental_advisory')
-            ? old('parental_advisory') === 'explicit'
-            : false
+        old('parental_advisory') && old('parental_advisory') === 'explicit',
     ) ?>
     <label for="explicit"><?= lang(
-        'Podcast.form.parental_advisory.explicit'
+        'Podcast.form.parental_advisory.explicit',
     ) ?></label>
 <?= form_fieldset_close() ?>
 
@@ -185,14 +181,14 @@
 
 <?= form_section(
     lang('Podcast.form.author_section_title'),
-    lang('Podcast.form.author_section_subtitle')
+    lang('Podcast.form.author_section_subtitle'),
 ) ?>
 
 <?= form_label(
     lang('Podcast.form.owner_name'),
     'owner_name',
     [],
-    lang('Podcast.form.owner_name_hint')
+    lang('Podcast.form.owner_name_hint'),
 ) ?>
 <?= form_input([
     'id' => 'owner_name',
@@ -206,7 +202,7 @@
     lang('Podcast.form.owner_email'),
     'owner_email',
     [],
-    lang('Podcast.form.owner_email_hint')
+    lang('Podcast.form.owner_email_hint'),
 ) ?>
 <?= form_input([
     'id' => 'owner_email',
@@ -222,7 +218,7 @@
     'publisher',
     [],
     lang('Podcast.form.publisher_hint'),
-    true
+    true,
 ) ?>
 <?= form_input([
     'id' => 'publisher',
@@ -243,7 +239,7 @@
 
 <?= form_section(
     lang('Podcast.form.location_section_title'),
-    lang('Podcast.form.location_section_subtitle')
+    lang('Podcast.form.location_section_subtitle'),
 ) ?>
 
 <?= form_label(
@@ -251,7 +247,7 @@
     'location_name',
     [],
     lang('Podcast.form.location_name_hint'),
-    true
+    true,
 ) ?>
 <?= form_input([
     'id' => 'location_name',
@@ -263,7 +259,7 @@
 
 <?= form_section(
     lang('Podcast.form.monetization_section_title'),
-    lang('Podcast.form.monetization_section_subtitle')
+    lang('Podcast.form.monetization_section_subtitle'),
 ) ?>
 
 <?= form_label(
@@ -271,7 +267,7 @@
     'payment_pointer',
     [],
     lang('Podcast.form.payment_pointer_hint'),
-    true
+    true,
 ) ?>
 <?= form_input([
     'id' => 'payment_pointer',
@@ -288,7 +284,7 @@
             'partner_id',
             [],
             lang('Podcast.form.partner_id_hint'),
-            true
+            true,
         ) ?>
         <?= form_input([
             'id' => 'partner_id',
@@ -303,7 +299,7 @@
             'partner_link_url',
             [],
             lang('Podcast.form.partner_link_url_hint'),
-            true
+            true,
         ) ?>
         <?= form_input([
             'id' => 'partner_link_url',
@@ -318,8 +314,7 @@
             'partner_image_url',
             [],
             lang('Podcast.form.partner_image_url_hint'),
-
-            true
+            true,
         ) ?>
         <?= form_input([
             'id' => 'partner_image_url',
@@ -334,14 +329,14 @@
 
 <?= form_section(
     lang('Podcast.form.advanced_section_title'),
-    lang('Podcast.form.advanced_section_subtitle')
+    lang('Podcast.form.advanced_section_subtitle'),
 ) ?>
 <?= form_label(
     lang('Podcast.form.custom_rss'),
     'custom_rss',
     [],
     lang('Podcast.form.custom_rss_hint'),
-    true
+    true,
 ) ?>
 <?= form_textarea([
     'id' => 'custom_rss',
@@ -353,7 +348,7 @@
 
 <?= form_section(
     lang('Podcast.form.status_section_title'),
-    lang('Podcast.form.status_section_subtitle')
+    lang('Podcast.form.status_section_subtitle'),
 ) ?>
 
 <?= form_switch(
@@ -361,7 +356,7 @@
     ['id' => 'block', 'name' => 'block'],
     'yes',
     old('block', false),
-    'mb-2'
+    'mb-2',
 ) ?>
 
 <?= form_switch(
@@ -369,7 +364,7 @@
     ['id' => 'complete', 'name' => 'complete'],
     'yes',
     old('complete', false),
-    'mb-2'
+    'mb-2',
 ) ?>
 
 <?= form_switch(
@@ -377,16 +372,16 @@
         hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'),
     ['id' => 'lock', 'name' => 'lock'],
     'yes',
-    old('lock', true)
+    old('lock', true),
 ) ?>
 
 <?= form_section_close() ?>
 
 <?= button(
     lang('Podcast.form.submit_create'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 
diff --git a/app/Views/admin/podcast/edit.php b/app/Views/admin/podcast/edit.php
index cd520c1010..c0ca9e64f6 100644
--- a/app/Views/admin/podcast/edit.php
+++ b/app/Views/admin/podcast/edit.php
@@ -379,7 +379,7 @@
 
 <?= button(
     lang('Podcast.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/podcast/import.php b/app/Views/admin/podcast/import.php
index c7a30c887f..258ba52fb7 100644
--- a/app/Views/admin/podcast/import.php
+++ b/app/Views/admin/podcast/import.php
@@ -108,7 +108,7 @@
                 'class' => 'form-radio text-pine-700',
             ],
             'title',
-            old('slug_field') ? old('slug_field') == 'title' : false,
+            old('slug_field') && old('slug_field') == 'title',
         ) ?>
         <span class="ml-2"><?= lang('PodcastImport.slug_field.title') ?></span>
     </label>
@@ -138,9 +138,7 @@
                 'class' => 'form-radio text-pine-600',
             ],
             'summary',
-            old('description_field')
-                ? old('description_field') == 'summary'
-                : false,
+            old('description_field') && old('description_field') == 'summary',
         ) ?>
         <span class="ml-2">&lt;itunes:summary&gt;</span>
     </label>
@@ -152,9 +150,8 @@
                 'class' => 'form-radio text-pine-700',
             ],
             'subtitle_summary',
-            old('description_field')
-                ? old('description_field') == 'subtitle_summary'
-                : false,
+            old('description_field') &&
+                old('description_field') == 'subtitle_summary',
         ) ?>
         <span class="ml-2">&lt;itunes:subtitle&gt; + &lt;itunes:summary&gt;</span>
     </label>
@@ -166,9 +163,7 @@
                 'class' => 'form-radio text-pine-700',
             ],
             'content',
-            old('description_field')
-                ? old('description_field') == 'content'
-                : false,
+            old('description_field') && old('description_field') == 'content',
         ) ?>
         <span class="ml-2">&lt;content:encoded&gt;</span>
     </label>
@@ -221,7 +216,7 @@
 
 <?= button(
     lang('PodcastImport.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/podcast/person.php b/app/Views/admin/podcast/person.php
index 1338f2ad1d..f0a5b99cf0 100644
--- a/app/Views/admin/podcast/person.php
+++ b/app/Views/admin/podcast/person.php
@@ -13,7 +13,7 @@
     lang('Person.create'),
     route_to('person-create'),
     ['variant' => 'primary', 'iconLeft' => 'add'],
-    ['class' => 'mr-2']
+    ['class' => 'mr-2'],
 ) ?>
 <?= $this->endSection() ?>
 
@@ -27,75 +27,78 @@
 
 <?php if ($podcastPersons): ?>
 
-<?= form_section(
-    lang('Person.podcast_form.manage_section_title'),
-    lang('Person.podcast_form.manage_section_subtitle')
-) ?>
+    <?= form_section(
+        lang('Person.podcast_form.manage_section_title'),
+        lang('Person.podcast_form.manage_section_subtitle'),
+    ) ?>
 
 
-<?= data_table(
-    [
+    <?= data_table(
         [
-            'header' => lang('Person.podcast_form.person'),
-            'cell' => function ($podcastPerson) {
-                return '<div class="flex">' .
-                    '<a href="' .
-                    route_to('person-view', $podcastPerson->person->id) .
-                    "\"><img src=\"{$podcastPerson->person->image->thumbnail_url}\" alt=\"{$podcastPerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" .
-                    '<div class="flex flex-col ml-3">' .
-                    $podcastPerson->person->full_name .
-                    ($podcastPerson->person_group && $podcastPerson->person_role
-                        ? '<span class="text-sm text-gray-600">' .
-                            lang(
-                                "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label"
-                            ) .
-                            ' â–¸ ' .
-                            lang(
-                                "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label"
-                            ) .
-                            '</span>'
-                        : '') .
-                    (empty($podcastPerson->person->information_url)
-                        ? ''
-                        : "<a href=\"{$podcastPerson->person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" .
-                            $podcastPerson->person->information_url .
-                            '</a>') .
-                    '</div></div>';
-            },
+            [
+                'header' => lang('Person.podcast_form.person'),
+                'cell' => function ($podcastPerson) {
+                    return '<div class="flex">' .
+                        '<a href="' .
+                        route_to('person-view', $podcastPerson->person->id) .
+                        "\"><img src=\"{$podcastPerson->person->image->thumbnail_url}\" alt=\"{$podcastPerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" .
+                        '<div class="flex flex-col ml-3">' .
+                        $podcastPerson->person->full_name .
+                        ($podcastPerson->person_group &&
+                        $podcastPerson->person_role
+                            ? '<span class="text-sm text-gray-600">' .
+                                lang(
+                                    "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label",
+                                ) .
+                                ' â–¸ ' .
+                                lang(
+                                    "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label",
+                                ) .
+                                '</span>'
+                            : '') .
+                        (empty($podcastPerson->person->information_url)
+                            ? ''
+                            : "<a href=\"{$podcastPerson->person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" .
+                                $podcastPerson->person->information_url .
+                                '</a>') .
+                        '</div></div>';
+                },
+            ],
+            [
+                'header' => lang('Common.actions'),
+                'cell' => function ($podcastPerson): string {
+                    return button(
+                        lang('Person.podcast_form.remove'),
+                        route_to(
+                            'podcast-person-remove',
+                            $podcastPerson->podcast_id,
+                            $podcastPerson->id,
+                        ),
+                        [
+                            'variant' => 'danger',
+                            'size' => 'small',
+                        ],
+                    );
+                },
+            ],
         ],
-        [
-            'header' => lang('Common.actions'),
-            'cell' => function ($podcastPerson) {
-                return button(
-                    lang('Person.podcast_form.remove'),
-                    route_to(
-                        'podcast-person-remove',
-                        $podcastPerson->podcast_id,
-                        $podcastPerson->id
-                    ),
-
-                    ['variant' => 'danger', 'size' => 'small']
-                );
-            },
-        ],
-    ],
-    $podcastPersons
-) ?>
+        $podcastPersons,
+    ) ?>
 
-<?= form_section_close() ?>
+    <?= form_section_close() ?>
 <?php endif; ?>
 
 
 <?= form_section(
     lang('Person.podcast_form.add_section_title'),
-    lang('Person.podcast_form.add_section_subtitle')
+    lang('Person.podcast_form.add_section_subtitle'),
 ) ?>
 
 <?= form_label(
     lang('Person.podcast_form.person'),
     'person',
     [],
-    lang('Person.podcast_form.person_hint')
+    lang('Person.podcast_form.person_hint'),
 ) ?>
 <?= form_multiselect('person[]', $personOptions, old('person', []), [
     'id' => 'person',
@@ -107,25 +110,26 @@
     lang('Person.podcast_form.group_role'),
     'group_role',
     [],
-
     lang('Person.podcast_form.group_role_hint'),
-    true
+    true,
 ) ?>
 <?= form_multiselect(
     'person_group_role[]',
     $taxonomyOptions,
     old('person_group_role', []),
-    ['id' => 'person_group_role', 'class' => 'form-select mb-4']
+    [
+        'id' => 'person_group_role',
+        'class' => 'form-select mb-4',
+    ],
 ) ?>
-        
-    
+
 <?= form_section_close() ?>
 <?= button(
     lang('Person.podcast_form.submit_add'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
-) ?> 
+    ['type' => 'submit', 'class' => 'self-end'],
+) ?>
 <?= form_close() ?>
 
 <?= $this->endSection() ?>
diff --git a/app/Views/admin/podcast/platforms.php b/app/Views/admin/podcast/platforms.php
index b41f8d5114..6cd06182d0 100644
--- a/app/Views/admin/podcast/platforms.php
+++ b/app/Views/admin/podcast/platforms.php
@@ -139,7 +139,7 @@
 
 <?= button(
     lang('Platforms.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/admin/user/create.php b/app/Views/admin/user/create.php
index aaf8369b52..afc93cb876 100644
--- a/app/Views/admin/user/create.php
+++ b/app/Views/admin/user/create.php
@@ -35,7 +35,6 @@
 <?= form_input([
     'id' => 'password',
     'name' => 'password',
-
     'class' => 'form-input mb-4',
     'type' => 'password',
     'autocomplete' => 'new-password',
@@ -43,9 +42,9 @@
 
 <?= button(
     lang('User.form.submit_create'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/user/edit.php b/app/Views/admin/user/edit.php
index a269314361..4fac32d99a 100644
--- a/app/Views/admin/user/edit.php
+++ b/app/Views/admin/user/edit.php
@@ -24,9 +24,9 @@
 
 <?= button(
     lang('User.form.submit_edit'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/admin/user/list.php b/app/Views/admin/user/list.php
index ebaed87d74..a7e34bea01 100644
--- a/app/Views/admin/user/list.php
+++ b/app/Views/admin/user/list.php
@@ -41,7 +41,7 @@
                         ]),
                         route_to('user-edit', $user->id),
                         ['variant' => 'info'],
-                        ['class' => 'ml-2']
+                        ['class' => 'ml-2'],
                     );
             },
         ],
@@ -63,26 +63,26 @@
                         'variant' => 'secondary',
                         'size' => 'small',
                     ],
-                    ['class' => 'mr-2']
+                    ['class' => 'mr-2'],
                 ) .
                     button(
                         lang('User.' . ($user->isBanned() ? 'unban' : 'ban')),
                         route_to(
                             $user->isBanned() ? 'user-unban' : 'user-ban',
-                            $user->id
+                            $user->id,
                         ),
                         ['variant' => 'warning', 'size' => 'small'],
-                        ['class' => 'mr-2']
+                        ['class' => 'mr-2'],
                     ) .
                     button(
                         lang('User.delete'),
                         route_to('user-delete', $user->id),
-                        ['variant' => 'danger', 'size' => 'small']
+                        ['variant' => 'danger', 'size' => 'small'],
                     );
             },
         ],
     ],
-    $users
+    $users,
 ) ?>
 
 <?= $this->endSection() ?>
diff --git a/app/Views/auth/emails/activation.php b/app/Views/auth/emails/activation.php
index d76eb17fcf..82a2032ff7 100644
--- a/app/Views/auth/emails/activation.php
+++ b/app/Views/auth/emails/activation.php
@@ -8,4 +8,4 @@
 
 <br>
 
-<p>If you did not registered on this website, you can safely ignore this email.</p>
\ No newline at end of file
+<p>If you did not registered on this website, you can safely ignore this email.</p>
diff --git a/app/Views/auth/forgot.php b/app/Views/auth/forgot.php
index 376fa93d9c..5f046c5180 100644
--- a/app/Views/auth/forgot.php
+++ b/app/Views/auth/forgot.php
@@ -24,9 +24,9 @@
 
 <?= button(
     lang('Auth.sendInstructions'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/auth/login.php b/app/Views/auth/login.php
index 4faf178575..1a6e810836 100644
--- a/app/Views/auth/login.php
+++ b/app/Views/auth/login.php
@@ -31,9 +31,9 @@
 
 <?= button(
     lang('Auth.loginAction'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
@@ -46,11 +46,11 @@
 <div class="flex flex-col items-center py-4 text-sm text-center">
     <?php if ($config->allowRegistration): ?>
         <a class="underline hover:no-underline" href="<?= route_to(
-            'register'
+            'register',
         ) ?>"><?= lang('Auth.needAnAccount') ?></a>
     <?php endif; ?>
     <a class="underline hover:no-underline" href="<?= route_to(
-        'forgot'
+        'forgot',
     ) ?>"><?= lang('Auth.forgotYourPassword') ?></a>
 </div>
 
diff --git a/app/Views/auth/register.php b/app/Views/auth/register.php
index de58efd46f..cf81efa4e1 100644
--- a/app/Views/auth/register.php
+++ b/app/Views/auth/register.php
@@ -46,9 +46,9 @@
 
 <?= button(
     lang('Auth.register'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
@@ -60,9 +60,9 @@
 
 <p class="py-4 text-sm text-center">
     <?= lang(
-        'Auth.alreadyRegistered'
+        'Auth.alreadyRegistered',
     ) ?> <a class="underline hover:no-underline" href="<?= route_to(
-     'login'
+     'login',
  ) ?>"><?= lang('Auth.signIn') ?></a>
 </p>
 
diff --git a/app/Views/auth/reset.php b/app/Views/auth/reset.php
index a89577aee2..429aba78f5 100644
--- a/app/Views/auth/reset.php
+++ b/app/Views/auth/reset.php
@@ -44,9 +44,9 @@
 
 <?= button(
     lang('Auth.resetPassword'),
-    null,
+    '',
     ['variant' => 'primary'],
-    ['type' => 'submit', 'class' => 'self-end']
+    ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
 
 <?= form_close() ?>
diff --git a/app/Views/credits.php b/app/Views/credits.php
index f85ddb070b..34344e76d4 100644
--- a/app/Views/credits.php
+++ b/app/Views/credits.php
@@ -7,45 +7,45 @@
 <?= $this->section('content') ?>
 
 <div class="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
-<?php foreach ($credits as $groupSlug => $groups): ?>
-    <?php if ($groupSlug): ?>
-    <div class="col-span-1 mt-12 mb-2 text-xl font-semibold text-gray-500 md:text-2xl md:col-span-2 "><?= $groups[
-        'group_label'
-    ] ?></div>
-    <?php endif; ?>
-    <?php foreach ($groups['persons'] as $personId => $persons): ?>
-        <div class="flex mt-2 mb-2">
-            <img
-            src="<?= $persons['thumbnail_url'] ?>"
-            alt="<?= $persons['full_name'] ?>"
-            class="object-cover w-16 h-16 border-4 rounded-full md:h-24 md:w-24 border-gray" />
-            <div class="flex flex-col ml-3 mr-4">
-                <span class="text-lg font-semibold text-gray-700 md:text-xl">
-                    <?= $persons['full_name'] ?>
-                </span>
-                <?php if (!empty($persons['information_url'])): ?>
-                <a href="<?= $persons['information_url'] ?>"
-                class="text-sm text-blue-800 hover:underline" target="_blank" rel="noreferrer noopener"><?= $persons[
-                    'information_url'
-                ] ?></a>
-                <?php endif; ?>
+    <?php foreach ($credits as $groupSlug => $groups): ?>
+        <?php if ($groupSlug): ?>
+            <div class="col-span-1 mt-12 mb-2 text-xl font-semibold text-gray-500 md:text-2xl md:col-span-2 "><?= $groups[
+                'group_label'
+            ] ?></div>
+        <?php endif; ?>
+        <?php foreach ($groups['persons'] as $persons): ?>
+            <div class="flex mt-2 mb-2">
+                <img src="<?= $persons['thumbnail_url'] ?>" alt="<?= $persons[
+    'full_name'
+] ?>" class="object-cover w-16 h-16 border-4 rounded-full md:h-24 md:w-24 border-gray" />
+                <div class="flex flex-col ml-3 mr-4">
+                    <span class="text-lg font-semibold text-gray-700 md:text-xl">
+                        <?= $persons['full_name'] ?>
+                    </span>
+                    <?php if (!empty($persons['information_url'])): ?>
+                        <a href="<?= $persons[
+                            'information_url'
+                        ] ?>" class="text-sm text-blue-800 hover:underline" target="_blank" rel="noreferrer noopener"><?= $persons[
+    'information_url'
+] ?></a>
+                    <?php endif; ?>
+                </div>
+            </div>
+            <div class="flex flex-col">
+                <?php foreach ($persons['roles'] as $role): ?>
+                    <?= $role['role_label'] ?>
+
+                    <?php foreach ($role['is_in'] as $in): ?>
+                        <a href="<?= $in[
+                            'link'
+                        ] ?>" class="text-sm text-gray-500 hover:underline"><?= $in[
+    'title'
+] ?></a>
+                    <?php endforeach; ?>
+                <?php endforeach; ?>
             </div>
-        </div>
-        <div class="flex flex-col">
-        <?php foreach ($persons['roles'] as $role_slug => $role_array): ?>
-            <?= $role_array['role_label'] ?>
-            
-            <?php foreach ($role_array['is_in'] as $isIn): ?>
-               <a
-               href="<?= $isIn['link'] ?>"
-               class="text-sm text-gray-500 hover:underline"><?= $isIn[
-                   'title'
-               ] ?></a>
-            <?php endforeach; ?>
         <?php endforeach; ?>
-        </div>
     <?php endforeach; ?>
-<?php endforeach; ?>
 </div>
 
 <?php $this->endSection(); ?>
diff --git a/app/Views/embeddable_player.php b/app/Views/embeddable_player.php
index 59fd77598d..e878a23753 100644
--- a/app/Views/embeddable_player.php
+++ b/app/Views/embeddable_player.php
@@ -60,4 +60,4 @@
     </div>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/app/Views/errors/cli/error_exception.php b/app/Views/errors/cli/error_exception.php
index f9ebc35a21..8fe9a97a22 100644
--- a/app/Views/errors/cli/error_exception.php
+++ b/app/Views/errors/cli/error_exception.php
@@ -12,8 +12,8 @@ CLI::write(
     'at ' .
         CLI::color(
             clean_path($exception->getFile()) . ':' . $exception->getLine(),
-            'green'
-        )
+            'green',
+        ),
 );
 CLI::newLine();
 
@@ -36,7 +36,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
             CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
         } else {
             CLI::write(
-                $c . $padFile . CLI::color('[internal function]', 'yellow')
+                $c . $padFile . CLI::color('[internal function]', 'yellow'),
             );
         }
 
@@ -60,13 +60,13 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
                     case is_object($value):
                         return 'Object(' . get_class($value) . ')';
                     case is_array($value):
-                        return count($value) ? '[...]' : '[]';
+                        return count($value) > 0 ? '[...]' : '[]';
                     case is_null($value):
                         return 'null'; // return the lowercased version
                     default:
                         return var_export($value, true);
                 }
-            }, array_values($error['args'] ?? []))
+            }, array_values($error['args'] ?? [])),
         );
 
         $function .= '(' . $args . ')';
diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php
index ae4d553d88..bee5dc32bb 100644
--- a/app/Views/errors/html/error_404.php
+++ b/app/Views/errors/html/error_404.php
@@ -5,25 +5,25 @@
 <head>
     <meta charset="utf-8">
     <title>404 Page Not Found</title>
-    <link rel="stylesheet" href="/assets/index.css"/>
+    <link rel="stylesheet" href="/assets/index.css" />
 </head>
 
 <body class="flex flex-col items-center justify-center min-h-screen px-2 text-center bg-gray-100">
-        <?= svg('castopod-mascot_confused', 'h-64') ?>
-        <h1 class="text-3xl font-bold font-display md:text-4xl lg:text-5xl">404 - File Not Found</h1>
+    <?= svg('castopod-mascot_confused', 'h-64') ?>
+    <h1 class="text-3xl font-bold font-display md:text-4xl lg:text-5xl">404 - File Not Found</h1>
 
-        <p class="mb-6 text-lg text-gray-600 md:text-xl lg:text-2xl">
-            <?php if (!empty($message) && $message !== '(null)'): ?>
-                <?= esc($message) ?>
-            <?php else: ?>
-                Sorry! Cannot seem to find the page you were looking for.
-            <?php endif; ?>
-        </p>
+    <p class="mb-6 text-lg text-gray-600 md:text-xl lg:text-2xl">
+        <?php if (!empty($message) && $message !== '(null)'): ?>
+            <?= esc($message) ?>
+        <?php else: ?>
+            Sorry! Cannot seem to find the page you were looking for.
+        <?php endif; ?>
+    </p>
 
-        <?= button(lang('Common.go_back'), previous_url(), [
-            'variant' => 'primary',
-            'iconLeft' => 'arrow-left',
-        ]) ?>
+    <?= button(lang('Common.go_back'), previous_url(), [
+        'variant' => 'primary',
+        'iconLeft' => 'arrow-left',
+    ]) ?>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php
index 7a1c053cd2..5ec36809c0 100644
--- a/app/Views/errors/html/error_exception.php
+++ b/app/Views/errors/html/error_exception.php
@@ -1,6 +1,13 @@
-<?php $error_id = uniqid('error', true); ?>
+<?php
+
+use Config\Services;
+use CodeIgniter\CodeIgniter;
+
+$error_id = uniqid('error', true);
+?>
 <!doctype html>
 <html>
+
 <head>
 	<meta charset="UTF-8">
 	<meta name="robots" content="noindex">
@@ -8,9 +15,9 @@
 	<title><?= esc($title) ?></title>
 	<style type="text/css">
 		<?= preg_replace(
-      '#[\r\n\t ]+#',
+      '~[\r\n\t ]+~',
       ' ',
-      file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')
+      file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css'),
   ) ?>
 	</style>
 
@@ -18,6 +25,7 @@
 		<?= file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.js') ?>
 	</script>
 </head>
+
 <body onload="init()">
 
 	<!-- Header -->
@@ -30,9 +38,8 @@
 				<a href="https://www.google.com/search?q=<?= urlencode(
         $title .
             ' ' .
-            preg_replace('#\'.*\'|".*"#Us', '', $exception->getMessage())
-    ) ?>"
-				   rel="noreferrer" target="_blank">search &rarr;</a>
+            preg_replace('~\'.*\'|".*"~Us', '', $exception->getMessage()),
+    ) ?>" rel="noreferrer" target="_blank">search &rarr;</a>
 			</p>
 		</div>
 	</div>
@@ -40,7 +47,7 @@
 	<!-- Source -->
 	<div class="container">
 		<p><b><?= esc(static::cleanPath($file, $line)) ?></b> at line <b><?= esc(
-    $line
+    $line,
 ) ?></b></p>
 
 		<?php if (is_file($file)): ?>
@@ -54,11 +61,11 @@
 
 		<ul class="tabs" id="tabs">
 			<li><a href="#backtrace">Backtrace</a></li>
-				<li><a href="#server">Server</a></li>
-				<li><a href="#request">Request</a></li>
-				<li><a href="#response">Response</a></li>
-				<li><a href="#files">Files</a></li>
-				<li><a href="#memory">Memory</a></li>
+			<li><a href="#server">Server</a></li>
+			<li><a href="#request">Request</a></li>
+			<li><a href="#response">Response</a></li>
+			<li><a href="#files">Files</a></li>
+			<li><a href="#memory">Memory</a></li>
 			</li>
 		</ul>
 
@@ -68,86 +75,88 @@
 			<div class="content" id="backtrace">
 
 				<ol class="trace">
-				<?php foreach ($trace as $index => $row): ?>
-
-					<li>
-						<p>
-							<!-- Trace info -->
-							<?php if (isset($row['file']) && is_file($row['file'])): ?>
-								<?php if (
-            isset($row['function']) &&
-            in_array(
-                $row['function'],
-                ['include', 'include_once', 'require', 'require_once'],
-                true
-            )
-        ) {
-            echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
-        } else {
-            echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
-        } ?>
-							<?php else: ?>
-								{PHP internal code}
-							<?php endif; ?>
-
-							<!-- Class/Method -->
-							<?php if (isset($row['class'])): ?>
-								&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc(
-            $row['class'] . $row['type'] . $row['function']
-        ) ?>
-								<?php if (!empty($row['args'])): ?>
-									<?php $args_id = $error_id . 'args' . $index; ?>
-									( <a href="#" onclick="return toggle('<?= esc(
-             $args_id,
-             'attr'
-         ) ?>');">arguments</a> )
-									<div class="args" id="<?= esc($args_id, 'attr') ?>">
-										<table cellspacing="0">
-
-										<?php
-          $params = null;
-          // Reflection by name is not available for closure function
-          if (substr($row['function'], -1) !== '}') {
-              $mirror = isset($row['class'])
-                  ? new \ReflectionMethod($row['class'], $row['function'])
-                  : new \ReflectionFunction($row['function']);
-              $params = $mirror->getParameters();
-          }
-          foreach ($row['args'] as $key => $value): ?>
-											<tr>
-												<td><code><?= esc(
-                isset($params[$key]) ? '$' . $params[$key]->name : "#$key"
-            ) ?></code></td>
-												<td><pre><?= esc(print_r($value, true)) ?></pre></td>
-											</tr>
-										<?php endforeach;
-          ?>
-
-										</table>
-									</div>
+					<?php foreach ($trace as $index => $row): ?>
+
+						<li>
+							<p>
+								<!-- Trace info -->
+								<?php if (isset($row['file']) && is_file($row['file'])): ?>
+									<?php if (
+             isset($row['function']) &&
+             in_array(
+                 $row['function'],
+                 ['include', 'include_once', 'require', 'require_once'],
+                 true,
+             )
+         ) {
+             echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
+         } else {
+             echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
+         } ?>
 								<?php else: ?>
-									()
+									{PHP internal code}
 								<?php endif; ?>
-							<?php endif; ?>
-
-							<?php if (!isset($row['class']) && isset($row['function'])): ?>
-								&nbsp;&nbsp;&mdash;&nbsp;&nbsp;	<?= esc($row['function']) ?>()
-							<?php endif; ?>
-						</p>
-
-						<!-- Source? -->
-						<?php if (
-          isset($row['file']) &&
-          is_file($row['file']) &&
-          isset($row['class'])
-      ): ?>
-							<div class="source">
-								<?= static::highlightFile($row['file'], $row['line']) ?>
+
+								<!-- Class/Method -->
+								<?php if (isset($row['class'])): ?>
+									&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc(
+             $row['class'] . $row['type'] . $row['function'],
+         ) ?>
+									<?php if (!empty($row['args'])): ?>
+										<?php $args_id = $error_id . 'args' . $index; ?>
+										( <a href="#" onclick="return toggle('<?= esc(
+              $args_id,
+              'attr',
+          ) ?>');">arguments</a> )
+							<div class="args" id="<?= esc($args_id, 'attr') ?>">
+								<table cellspacing="0">
+
+									<?php
+         $params = null;
+         // Reflection by name is not available for closure function
+         if (substr($row['function'], -1) !== '}') {
+             $mirror = isset($row['class'])
+                 ? new ReflectionMethod($row['class'], $row['function'])
+                 : new ReflectionFunction($row['function']);
+             $params = $mirror->getParameters();
+         }
+         foreach ($row['args'] as $key => $value): ?>
+										<tr>
+											<td><code><?= esc(
+               isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}",
+           ) ?></code></td>
+											<td>
+												<pre><?= esc(print_r($value, true)) ?></pre>
+											</td>
+										</tr>
+									<?php endforeach;
+         ?>
+
+								</table>
 							</div>
+						<?php else: ?>
+							()
 						<?php endif; ?>
-					</li>
-
-				<?php endforeach; ?>
+					<?php endif; ?>
+
+					<?php if (!isset($row['class']) && isset($row['function'])): ?>
+						&nbsp;&nbsp;&mdash;&nbsp;&nbsp; <?= esc($row['function']) ?>()
+					<?php endif; ?>
+					</p>
+
+					<!-- Source? -->
+					<?php if (
+         isset($row['file']) &&
+         is_file($row['file']) &&
+         isset($row['class'])
+     ): ?>
+						<div class="source">
+							<?= static::highlightFile($row['file'], $row['line']) ?>
+						</div>
+					<?php endif; ?>
+						</li>
+
+					<?php endforeach; ?>
 				</ol>
 
 			</div>
@@ -169,18 +178,18 @@
 							</tr>
 						</thead>
 						<tbody>
-						<?php foreach ($GLOBALS[$var] as $key => $value): ?>
-							<tr>
-								<td><?= esc($key) ?></td>
-								<td>
-									<?php if (is_string($value)): ?>
-										<?= esc($value) ?>
-									<?php else: ?>
-										<pre><?= esc(print_r($value, true)) ?></pre>
-									<?php endif; ?>
-								</td>
-							</tr>
-						<?php endforeach; ?>
+							<?php foreach ($GLOBALS[$var] as $key => $value): ?>
+								<tr>
+									<td><?= esc($key) ?></td>
+									<td>
+										<?php if (is_string($value)): ?>
+											<?= esc($value) ?>
+										<?php else: ?>
+											<pre><?= esc(print_r($value, true)) ?></pre>
+										<?php endif; ?>
+									</td>
+								</tr>
+							<?php endforeach; ?>
 						</tbody>
 					</table>
 
@@ -199,18 +208,18 @@
 							</tr>
 						</thead>
 						<tbody>
-						<?php foreach ($constants['user'] as $key => $value): ?>
-							<tr>
-								<td><?= esc($key) ?></td>
-								<td>
-									<?php if (is_string($value)): ?>
-										<?= esc($value) ?>
-									<?php else: ?>
-										<pre><?= esc(print_r($value, true)) ?></pre>
-									<?php endif; ?>
-								</td>
-							</tr>
-						<?php endforeach; ?>
+							<?php foreach ($constants['user'] as $key => $value): ?>
+								<tr>
+									<td><?= esc($key) ?></td>
+									<td>
+										<?php if (is_string($value)): ?>
+											<?= esc($value) ?>
+										<?php else: ?>
+											<pre><?= esc(print_r($value, true)) ?></pre>
+										<?php endif; ?>
+									</td>
+								</tr>
+							<?php endforeach; ?>
 						</tbody>
 					</table>
 				<?php endif; ?>
@@ -218,7 +227,7 @@
 
 			<!-- Request -->
 			<div class="content" id="request">
-				<?php $request = \Config\Services::request(); ?>
+				<?php $request = Services::request(); ?>
 
 				<table>
 					<tbody>
@@ -257,9 +266,13 @@
 
 				<?php $empty = true; ?>
 				<?php foreach (['_GET', '_POST', '_COOKIE'] as $var): ?>
-					<?php if (empty($GLOBALS[$var]) || !is_array($GLOBALS[$var])) {
-         continue;
-     } ?>
+
+					if (empty($GLOBALS[$var])) {
+					continue;
+					}
+					if (!is_array($GLOBALS[$var])) {
+					continue;
+					} ?>
 
 					<?php $empty = false; ?>
 
@@ -273,18 +286,18 @@
 							</tr>
 						</thead>
 						<tbody>
-						<?php foreach ($GLOBALS[$var] as $key => $value): ?>
-							<tr>
-								<td><?= esc($key) ?></td>
-								<td>
-									<?php if (is_string($value)): ?>
-										<?= esc($value) ?>
-									<?php else: ?>
-										<pre><?= esc(print_r($value, true)) ?></pre>
-									<?php endif; ?>
-								</td>
-							</tr>
-						<?php endforeach; ?>
+							<?php foreach ($GLOBALS[$var] as $key => $value): ?>
+								<tr>
+									<td><?= esc($key) ?></td>
+									<td>
+										<?php if (is_string($value)): ?>
+											<?= esc($value) ?>
+										<?php else: ?>
+											<pre><?= esc(print_r($value, true)) ?></pre>
+										<?php endif; ?>
+									</td>
+								</tr>
+							<?php endforeach; ?>
 						</tbody>
 					</table>
 
@@ -311,20 +324,20 @@
 							</tr>
 						</thead>
 						<tbody>
-						<?php foreach ($headers as $value): ?>
-							<?php if (empty($value)) {
-           continue;
-       } ?>
-							<?php if (!is_array($value)) {
-           $value = [$value];
-       } ?>
-							<?php foreach ($value as $h): ?>
-								<tr>
-									<td><?= esc($h->getName(), 'html') ?></td>
-									<td><?= esc($h->getValueLine(), 'html') ?></td>
-								</tr>
+							<?php foreach ($headers as $value): ?>
+								<?php if (empty($value)) {
+            continue;
+        } ?>
+								<?php if (!is_array($value)) {
+            $value = [$value];
+        } ?>
+								<?php foreach ($value as $h): ?>
+									<tr>
+										<td><?= esc($h->getName(), 'html') ?></td>
+										<td><?= esc($h->getValueLine(), 'html') ?></td>
+									</tr>
+								<?php endforeach; ?>
 							<?php endforeach; ?>
-						<?php endforeach; ?>
 						</tbody>
 					</table>
 
@@ -333,7 +346,7 @@
 
 			<!-- Response -->
 			<?php
-   $response = \Config\Services::response();
+   $response = Services::response();
    $response->setStatusCode(http_response_code());
    ?>
 			<div class="content" id="response">
@@ -358,12 +371,12 @@
 							</tr>
 						</thead>
 						<tbody>
-						<?php foreach ($headers as $name => $value): ?>
-							<tr>
-								<td><?= esc($name, 'html') ?></td>
-								<td><?= esc($response->getHeaderLine($name), 'html') ?></td>
-							</tr>
-						<?php endforeach; ?>
+							<?php foreach (array_keys($headers) as $name): ?>
+								<tr>
+									<td><?= esc($name, 'html') ?></td>
+									<td><?= esc($response->getHeaderLine($name), 'html') ?></td>
+								</tr>
+							<?php endforeach; ?>
 						</tbody>
 					</table>
 
@@ -375,9 +388,9 @@
 				<?php $files = get_included_files(); ?>
 
 				<ol>
-				<?php foreach ($files as $file): ?>
-					<li><?= esc(static::cleanPath($file)) ?></li>
-				<?php endforeach; ?>
+					<?php foreach ($files as $file): ?>
+						<li><?= esc(static::cleanPath($file)) ?></li>
+					<?php endforeach; ?>
 				</ol>
 			</div>
 
@@ -403,7 +416,7 @@
 
 			</div>
 
-		</div>  <!-- /tab-content -->
+		</div> <!-- /tab-content -->
 
 	</div> <!-- /container -->
 
@@ -412,12 +425,13 @@
 
 			<p>
 				Displayed at <?= esc(date('H:i:sa')) ?> &mdash;
-				PHP: <?= esc(phpversion()) ?>  &mdash;
-				CodeIgniter: <?= esc(\CodeIgniter\CodeIgniter::CI_VERSION) ?>
+				PHP: <?= esc(phpversion()) ?> &mdash;
+				CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?>
 			</p>
 
 		</div>
 	</div>
 
 </body>
-</html>
\ No newline at end of file
+
+</html>
diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php
index 4125e0c9d8..f7bdb022d2 100644
--- a/app/Views/errors/html/production.php
+++ b/app/Views/errors/html/production.php
@@ -7,7 +7,7 @@
 	<meta name="robots" content="noindex">
 
 	<title>Whoops!</title>
-    <link rel="stylesheet" href="/assets/index.css"/>
+	<link rel="stylesheet" href="/assets/index.css" />
 </head>
 
 <body class="flex flex-col items-center justify-center min-h-screen px-2 text-center bg-gray-100">
@@ -16,4 +16,4 @@
 	<p class="mb-6 text-lg text-gray-600 md:text-xl lg:text-2xl">We seem to have hit a snag. Please try again later...</p>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/app/Views/install/cache_config.php b/app/Views/install/cache_config.php
index 0cb1fc80a8..3a2543033c 100644
--- a/app/Views/install/cache_config.php
+++ b/app/Views/install/cache_config.php
@@ -34,7 +34,7 @@
 
 <?= button(
     lang('Install.form.next') . icon('arrow-right', 'ml-2'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/install/create_superadmin.php b/app/Views/install/create_superadmin.php
index 9bf89683c1..4363cfd968 100644
--- a/app/Views/install/create_superadmin.php
+++ b/app/Views/install/create_superadmin.php
@@ -42,7 +42,7 @@
 
 <?= button(
     icon('check', 'mr-2') . lang('Install.form.submit'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/install/database_config.php b/app/Views/install/database_config.php
index e50d584e69..5a887dd65b 100644
--- a/app/Views/install/database_config.php
+++ b/app/Views/install/database_config.php
@@ -70,7 +70,7 @@
 
 <?= button(
     lang('Install.form.next') . icon('arrow-right', 'ml-2'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/install/instance_config.php b/app/Views/install/instance_config.php
index 4c7ff578a3..f9d1bc95c4 100644
--- a/app/Views/install/instance_config.php
+++ b/app/Views/install/instance_config.php
@@ -67,7 +67,7 @@
 
 <?= button(
     lang('Install.form.next') . icon('arrow-right', 'ml-2'),
-    null,
+    '',
     ['variant' => 'primary'],
     ['type' => 'submit', 'class' => 'self-end'],
 ) ?>
diff --git a/app/Views/page.php b/app/Views/page.php
index d8aef5572b..77a04ba88b 100644
--- a/app/Views/page.php
+++ b/app/Views/page.php
@@ -16,7 +16,7 @@
             <a href="<?= route_to('home') ?>"
             class="inline-flex items-center mb-2"><?= icon(
                 'arrow-left',
-                'mr-2'
+                'mr-2',
             ) . lang('Page.back_to_home') ?></a>
             <h1 class="text-3xl font-semibold"><?= $page->title ?></h1>
         </div>
diff --git a/app/Views/pager/default_full.php b/app/Views/pager/default_full.php
index ab90a46fc6..7c4c64cb24 100644
--- a/app/Views/pager/default_full.php
+++ b/app/Views/pager/default_full.php
@@ -1,31 +1,32 @@
 <?php
 
+use CodeIgniter\Pager\PagerRenderer;
 /**
  * @copyright  2020 Podlibre
  * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
  * @link       https://castopod.org/
  *
- * @var \CodeIgniter\Pager\PagerRenderer $pager
+ * @var PagerRenderer $pager
  */
-
-$pager->setSurroundCount(2); ?>
+$pager->setSurroundCount(2);
+?>
 
 <nav aria-label="<?= lang('Pager.pageNavigation') ?>">
     <ul class="flex justify-center">
         <?php if ($pager->hasPreviousPage()): ?>
             <li>
                 <a href="<?= $pager->getFirst() ?>" aria-label="<?= lang(
-    'Pager.first'
+    'Pager.first',
 ) ?>"  class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
                     <span aria-hidden="true"><?= lang('Pager.first') ?></span>
                 </a>
             </li>
             <li>
                 <a href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang(
-    'Pager.previous'
+    'Pager.previous',
 ) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
                     <span aria-hidden="true"><?= lang(
-                        'Pager.previous'
+                        'Pager.previous',
                     ) ?></span>
                 </a>
             </li>
@@ -50,14 +51,14 @@ $pager->setSurroundCount(2); ?>
         <?php if ($pager->hasNextPage()): ?>
             <li>
                 <a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang(
-    'Pager.next'
+    'Pager.next',
 ) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
                     <span aria-hidden="true"><?= lang('Pager.next') ?></span>
                 </a>
             </li>
             <li>
                 <a href="<?= $pager->getLast() ?>" aria-label="<?= lang(
-    'Pager.last'
+    'Pager.last',
 ) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
                     <span aria-hidden="true"><?= lang('Pager.last') ?></span>
                 </a>
diff --git a/app/Views/podcast/_layout_authenticated.php b/app/Views/podcast/_layout_authenticated.php
index 102929606d..b0ee155529 100644
--- a/app/Views/podcast/_layout_authenticated.php
+++ b/app/Views/podcast/_layout_authenticated.php
@@ -144,4 +144,4 @@
             </div>
         </div>
     </div>
-</body>
\ No newline at end of file
+</body>
diff --git a/app/Views/podcast/_partials/header.php b/app/Views/podcast/_partials/header.php
index 48ce183d12..7924defeca 100644
--- a/app/Views/podcast/_partials/header.php
+++ b/app/Views/podcast/_partials/header.php
@@ -92,4 +92,4 @@
 ]) ?></a>
         </div>
     </div>
-</header>
\ No newline at end of file
+</header>
diff --git a/app/Views/podcast/_partials/note.php b/app/Views/podcast/_partials/note.php
index c14fbef75a..5111dd1ea7 100644
--- a/app/Views/podcast/_partials/note.php
+++ b/app/Views/podcast/_partials/note.php
@@ -4,17 +4,17 @@
             ->avatar_image_url ?>" alt="<?= $note->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
         <div class="flex flex-col min-w-0">
             <a href="<?= $note->actor
-                ->uri ?>" class="flex items-baseline hover:underline" <?= !$note
+                ->uri ?>" class="flex items-baseline hover:underline" <?= $note
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>>
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>>
                 <span class="mr-2 font-semibold truncate"><?= $note->actor
                     ->display_name ?></span>
                 <span class="text-sm text-gray-500 truncate">@<?= $note->actor
                     ->username .
-                    (!$note->actor->is_local
-                        ? '@' . $note->actor->domain
-                        : '') ?></span>
+                    ($note->actor->is_local
+                        ? ''
+                        : '@' . $note->actor->domain) ?></span>
             </a>
             <a href="<?= route_to('note', $podcast->name, $note->id) ?>"
             class="text-xs text-gray-500">
diff --git a/app/Views/podcast/_partials/note_actions_authenticated.php b/app/Views/podcast/_partials/note_actions_authenticated.php
index 61248ab8b4..59fdf37071 100644
--- a/app/Views/podcast/_partials/note_actions_authenticated.php
+++ b/app/Views/podcast/_partials/note_actions_authenticated.php
@@ -86,4 +86,4 @@
             </form>
         <?php endif; ?>
     </nav>
-</footer>
\ No newline at end of file
+</footer>
diff --git a/app/Views/podcast/_partials/note_authenticated.php b/app/Views/podcast/_partials/note_authenticated.php
index baeb8794a4..c60f594274 100644
--- a/app/Views/podcast/_partials/note_authenticated.php
+++ b/app/Views/podcast/_partials/note_authenticated.php
@@ -4,17 +4,17 @@
             ->avatar_image_url ?>" alt="<?= $note->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
         <div class="flex flex-col min-w-0">
             <a href="<?= $note->actor
-                ->uri ?>" class="flex items-baseline hover:underline" <?= !$note
+                ->uri ?>" class="flex items-baseline hover:underline" <?= $note
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>>
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>>
                 <span class="mr-2 font-semibold truncate"><?= $note->actor
                     ->display_name ?></span>
                 <span class="text-sm text-gray-500 truncate">@<?= $note->actor
                     ->username .
-                    (!$note->actor->is_local
-                        ? '@' . $note->actor->domain
-                        : '') ?></span>
+                    ($note->actor->is_local
+                        ? ''
+                        : '@' . $note->actor->domain) ?></span>
             </a>
             <a href="<?= route_to('note', $podcast->name, $note->id) ?>"
             class="text-xs text-gray-500">
diff --git a/app/Views/podcast/_partials/note_with_replies_authenticated.php b/app/Views/podcast/_partials/note_with_replies_authenticated.php
index 95e7fe7258..f91ef54013 100644
--- a/app/Views/podcast/_partials/note_with_replies_authenticated.php
+++ b/app/Views/podcast/_partials/note_with_replies_authenticated.php
@@ -27,7 +27,7 @@
 ) ?>
 <?= button(
     lang('Note.form.submit_reply'),
-    null,
+    '',
     ['variant' => 'primary', 'size' => 'small'],
     [
         'type' => 'submit',
diff --git a/app/Views/podcast/_partials/reblog.php b/app/Views/podcast/_partials/reblog.php
index 461c76ca64..65d6d9c150 100644
--- a/app/Views/podcast/_partials/reblog.php
+++ b/app/Views/podcast/_partials/reblog.php
@@ -11,17 +11,17 @@
             ->avatar_image_url ?>" alt="<?= $note->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
         <div class="flex flex-col min-w-0">
             <a href="<?= $note->actor
-                ->uri ?>" class="flex items-baseline hover:underline" <?= !$note
+                ->uri ?>" class="flex items-baseline hover:underline" <?= $note
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>>
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>>
                 <span class="mr-2 font-semibold truncate"><?= $note->actor
                     ->display_name ?></span>
                 <span class="text-sm text-gray-500 truncate">@<?= $note->actor
                     ->username .
-                    (!$note->actor->is_local
-                        ? '@' . $note->actor->domain
-                        : '') ?></span>
+                    ($note->actor->is_local
+                        ? ''
+                        : '@' . $note->actor->domain) ?></span>
             </a>
             <a href="<?= route_to('note', $podcast->name, $note->id) ?>"
             class="text-xs text-gray-500">
diff --git a/app/Views/podcast/_partials/reblog_authenticated.php b/app/Views/podcast/_partials/reblog_authenticated.php
index e9251e256a..80c2431e0a 100644
--- a/app/Views/podcast/_partials/reblog_authenticated.php
+++ b/app/Views/podcast/_partials/reblog_authenticated.php
@@ -11,17 +11,17 @@
             ->avatar_image_url ?>" alt="<?= $note->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
         <div class="flex flex-col min-w-0">
             <a href="<?= $note->actor
-                ->uri ?>" class="flex items-baseline hover:underline" <?= !$note
+                ->uri ?>" class="flex items-baseline hover:underline" <?= $note
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>>
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>>
                 <span class="mr-2 font-semibold truncate"><?= $note->actor
                     ->display_name ?></span>
                 <span class="text-sm text-gray-500 truncate">@<?= $note->actor
                     ->username .
-                    (!$note->actor->is_local
-                        ? '@' . $note->actor->domain
-                        : '') ?></span>
+                    ($note->actor->is_local
+                        ? ''
+                        : '@' . $note->actor->domain) ?></span>
             </a>
             <a href="<?= route_to('note', $podcast->name, $note->id) ?>"
             class="text-xs text-gray-500">
diff --git a/app/Views/podcast/_partials/reply.php b/app/Views/podcast/_partials/reply.php
index 2a9018edf1..8476bfd462 100644
--- a/app/Views/podcast/_partials/reply.php
+++ b/app/Views/podcast/_partials/reply.php
@@ -4,13 +4,13 @@
     <div class="flex flex-col flex-1 min-w-0">
         <header class="flex items-center mb-2">
             <a href="<?= $reply->actor
-                ->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= !$reply
+                ->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= $reply
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>><?= $reply->actor
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>><?= $reply->actor
     ->display_name ?><span class="ml-1 text-sm font-normal text-gray-600">@<?= $reply
     ->actor->username .
-    (!$reply->actor->is_local ? '@' . $reply->actor->domain : '') ?></span></a>
+    ($reply->actor->is_local ? '' : '@' . $reply->actor->domain) ?></span></a>
             <time
             class="flex-shrink-0 ml-auto text-xs text-gray-600"
             itemprop="published"
diff --git a/app/Views/podcast/_partials/reply_actions.php b/app/Views/podcast/_partials/reply_actions.php
index 99895321cd..572637d719 100644
--- a/app/Views/podcast/_partials/reply_actions.php
+++ b/app/Views/podcast/_partials/reply_actions.php
@@ -33,4 +33,4 @@
             ]),
         ],
     ) ?>
-</footer>
\ No newline at end of file
+</footer>
diff --git a/app/Views/podcast/_partials/reply_actions_authenticated.php b/app/Views/podcast/_partials/reply_actions_authenticated.php
index 4700eaef01..b669cab072 100644
--- a/app/Views/podcast/_partials/reply_actions_authenticated.php
+++ b/app/Views/podcast/_partials/reply_actions_authenticated.php
@@ -86,4 +86,4 @@
             </form>
         <?php endif; ?>
     </nav>
-</footer>
\ No newline at end of file
+</footer>
diff --git a/app/Views/podcast/_partials/reply_authenticated.php b/app/Views/podcast/_partials/reply_authenticated.php
index f618dc0be6..62eab74504 100644
--- a/app/Views/podcast/_partials/reply_authenticated.php
+++ b/app/Views/podcast/_partials/reply_authenticated.php
@@ -4,13 +4,13 @@
     <div class="flex flex-col flex-1 min-w-0">
         <header class="flex items-center mb-2">
             <a href="<?= $reply->actor
-                ->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= !$reply
+                ->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= $reply
     ->actor->is_local
-    ? 'target="_blank" rel="noopener noreferrer"'
-    : '' ?>><?= $reply->actor
+    ? ''
+    : 'target="_blank" rel="noopener noreferrer"' ?>><?= $reply->actor
     ->display_name ?><span class="ml-1 text-sm font-normal text-gray-600">@<?= $reply
     ->actor->username .
-    (!$reply->actor->is_local ? '@' . $reply->actor->domain : '') ?></span></a>
+    ($reply->actor->is_local ? '' : '@' . $reply->actor->domain) ?></span></a>
             <time
             class="flex-shrink-0 ml-auto text-xs text-gray-600"
             itemprop="published"
diff --git a/app/Views/podcast/_partials/sidebar.php b/app/Views/podcast/_partials/sidebar.php
index a66854e241..9041ee2871 100644
--- a/app/Views/podcast/_partials/sidebar.php
+++ b/app/Views/podcast/_partials/sidebar.php
@@ -1,9 +1,6 @@
 <aside id="main-sidebar" class="fixed top-0 right-0 flex flex-col items-start flex-shrink-0 w-64 h-screen px-6 py-4 overflow-y-auto transform translate-x-full lg:sticky lg:translate-x-0">
     <?php if (
-        array_search(
-            true,
-            array_column($podcast->fundingPlatforms, 'is_visible'),
-        ) !== false
+        in_array(true, array_column($podcast->fundingPlatforms, 'is_visible'))
     ): ?>
     <h2 class="mb-2 text-sm font-semibold"><?= lang(
         'Podcast.sponsor_title',
@@ -16,10 +13,7 @@
     <?php endif; ?>
 
     <?php if (
-        array_search(
-            true,
-            array_column($podcast->socialPlatforms, 'is_visible'),
-        ) !== false
+        in_array(true, array_column($podcast->socialPlatforms, 'is_visible'))
     ): ?>
     <h2 class="mb-2 text-sm font-semibold"> <?= lang('Podcast.find_on', [
         'podcastTitle' => $podcast->title,
diff --git a/app/Views/podcast/activity_authenticated.php b/app/Views/podcast/activity_authenticated.php
index f64cbc775b..bffb4f4cb6 100644
--- a/app/Views/podcast/activity_authenticated.php
+++ b/app/Views/podcast/activity_authenticated.php
@@ -75,7 +75,7 @@
 
         <?= button(
             lang('Note.form.submit'),
-            null,
+            '',
             ['variant' => 'primary', 'size' => 'small'],
             ['type' => 'submit', 'class' => 'self-end'],
         ) ?>
diff --git a/app/Views/podcast/episode_authenticated.php b/app/Views/podcast/episode_authenticated.php
index 38ea9ab811..ddedfbc78d 100644
--- a/app/Views/podcast/episode_authenticated.php
+++ b/app/Views/podcast/episode_authenticated.php
@@ -196,7 +196,7 @@
                     ]) ?>
                     <?= button(
                         lang('Note.form.submit'),
-                        null,
+                        '',
                         ['variant' => 'primary', 'size' => 'small'],
                         ['type' => 'submit', 'class' => 'self-end'],
                     ) ?>
diff --git a/app/Views/podcast/follow.php b/app/Views/podcast/follow.php
index 8a564a2eec..8bd25ccd12 100644
--- a/app/Views/podcast/follow.php
+++ b/app/Views/podcast/follow.php
@@ -67,7 +67,7 @@
 
         <?= button(
             lang('ActivityPub.follow.submit'),
-            null,
+            '',
             ['variant' => 'primary'],
             ['type' => 'submit', 'class' => 'self-end'],
         ) ?>
@@ -84,4 +84,4 @@
             ]) ?>
         </p>
     </footer>
-</body>
\ No newline at end of file
+</body>
diff --git a/app/Views/podcast/note_remote_action.php b/app/Views/podcast/note_remote_action.php
index 99a4cd92f5..b457047241 100644
--- a/app/Views/podcast/note_remote_action.php
+++ b/app/Views/podcast/note_remote_action.php
@@ -60,7 +60,7 @@
 
         <?= button(
             lang('ActivityPub.' . $action . '.submit'),
-            null,
+            '',
             ['variant' => 'primary'],
             ['type' => 'submit', 'class' => 'self-end'],
         ) ?>
diff --git a/captainhook.json b/captainhook.json
new file mode 100644
index 0000000000..cdc877e729
--- /dev/null
+++ b/captainhook.json
@@ -0,0 +1,37 @@
+{
+  "pre-commit": {
+    "enabled": true,
+    "actions": [
+      {
+        "action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting",
+        "options": [],
+        "conditions": []
+      },
+      {
+        "action": "composer phpcs -- {$STAGED_FILES|of-type:php}",
+        "options": [],
+        "conditions": [
+          {
+            "exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType",
+            "args": ["php"]
+          }
+        ]
+      }
+    ]
+  },
+  "pre-push": {
+    "enabled": true,
+    "actions": [
+      {
+        "action": "composer test -- --no-coverage",
+        "options": [],
+        "conditions": []
+      },
+      {
+        "action": "composer rector",
+        "options": [],
+        "conditions": []
+      }
+    ]
+  }
+}
diff --git a/composer.json b/composer.json
index 9342d50d7e..39fec8a906 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,9 @@
   "require-dev": {
     "mikey179/vfsstream": "^v1.6.8",
     "phpunit/phpunit": "^9.5.1",
-    "squizlabs/php_codesniffer": "3.5.8"
+    "squizlabs/php_codesniffer": "3.5.8",
+    "rector/rector": "^0.10.17",
+    "captainhook/captainhook": "^5.9"
   },
   "autoload": {
     "psr-4": {
@@ -42,7 +44,10 @@
     }
   },
   "scripts": {
-    "test": "phpunit",
+    "test": "vendor/bin/phpunit",
+    "rector": "vendor/bin/rector process --dry-run --ansi",
+    "rector:fix": "vendor/bin/rector process --ansi",
+    "phpcs": "vendor/bin/phpcbf --standard=.phpcs.xml --encoding=utf-8 -n -p",
     "post-install-cmd": [
       "@php vendor/opawg/user-agents-php/src/UserAgentsGenerate.php >  vendor/opawg/user-agents-php/src/UserAgents.php",
       "@php vendor/opawg/user-agents-php/src/UserAgentsRSSGenerate.php >  vendor/opawg/user-agents-php/src/UserAgentsRSS.php",
@@ -59,7 +64,8 @@
       "@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json >  app/Language/en/PersonsTaxonomy.php",
       "@php vendor/podlibre/podcast-namespace/src/TaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-fr.json >  app/Language/fr/PersonsTaxonomy.php",
       "@php vendor/podlibre/podcast-namespace/src/ReversedTaxonomyGenerate.php https://raw.githubusercontent.com/Podcastindex-org/podcast-namespace/main/taxonomy-en.json >  vendor/podlibre/podcast-namespace/src/ReversedTaxonomy.php"
-    ]
+    ],
+    "post-autoload-dump": "vendor/bin/captainhook install -f -s"
   },
   "support": {
     "source": "https://code.podlibre.org/podlibre/castopod-host.git",
@@ -70,7 +76,7 @@
   "repositories": [
     {
       "type": "vcs",
-      "url": "https://code.podlibre.org/podlibre/castopod-host.git"
+      "url": "https://github.com/codeigniter4/codeigniter4"
     }
   ]
 }
diff --git a/composer.lock b/composer.lock
index 89b57d985e..d694999539 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "f370b196462e2ca2ff3e2df9627f2ba4",
+    "content-hash": "4873e8d92c9c1829e6f02d8db590a786",
     "packages": [
         {
             "name": "brick/math",
@@ -68,12 +68,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/codeigniter4/CodeIgniter4.git",
-                "reference": "8b2e7c29043977fac378c37690cc951a715c2bd5"
+                "reference": "7546ee04a55071d4731c77346deb47843f397c87"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/8b2e7c29043977fac378c37690cc951a715c2bd5",
-                "reference": "8b2e7c29043977fac378c37690cc951a715c2bd5",
+                "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/7546ee04a55071d4731c77346deb47843f397c87",
+                "reference": "7546ee04a55071d4731c77346deb47843f397c87",
                 "shasum": ""
             },
             "require": {
@@ -91,10 +91,11 @@
                 "fakerphp/faker": "^1.9",
                 "mikey179/vfsstream": "^1.6",
                 "nexusphp/tachycardia": "^1.0",
+                "nikic/php-parser": "4.10.4",
                 "phpstan/phpstan": "0.12.85",
                 "phpunit/phpunit": "^9.1",
                 "predis/predis": "^1.1",
-                "rector/rector": "0.10.17",
+                "rector/rector": "0.10.19",
                 "squizlabs/php_codesniffer": "^3.3"
             },
             "suggest": {
@@ -139,7 +140,7 @@
                 "slack": "https://codeigniterchat.slack.com",
                 "issues": "https://github.com/codeigniter4/CodeIgniter4/issues"
             },
-            "time": "2021-05-03T08:32:21+00:00"
+            "time": "2021-05-06T01:16:55+00:00"
         },
         {
             "name": "composer/ca-bundle",
@@ -2190,35 +2191,53 @@
     ],
     "packages-dev": [
         {
-            "name": "doctrine/instantiator",
-            "version": "1.4.0",
+            "name": "captainhook/captainhook",
+            "version": "5.9.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
+                "url": "https://github.com/captainhookphp/captainhook.git",
+                "reference": "76987f60c5c77d106ec6b589e4d658d7a2a7d6f5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
-                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
+                "url": "https://api.github.com/repos/captainhookphp/captainhook/zipball/76987f60c5c77d106ec6b589e4d658d7a2a7d6f5",
+                "reference": "76987f60c5c77d106ec6b589e4d658d7a2a7d6f5",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1 || ^8.0"
+                "ext-json": "*",
+                "ext-spl": "*",
+                "ext-xml": "*",
+                "php": ">=7.2",
+                "sebastianfeldmann/camino": "^0.9.2",
+                "sebastianfeldmann/cli": "^3.3",
+                "sebastianfeldmann/git": "^3.7",
+                "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0",
+                "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0",
+                "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0"
+            },
+            "replace": {
+                "sebastianfeldmann/captainhook": "*"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^8.0",
-                "ext-pdo": "*",
-                "ext-phar": "*",
-                "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
-                "phpstan/phpstan": "^0.12",
-                "phpstan/phpstan-phpunit": "^0.12",
-                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+                "composer/composer": "~1",
+                "mikey179/vfsstream": "~1"
             },
+            "bin": [
+                "bin/captainhook"
+            ],
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "6.0.x-dev"
+                },
+                "captainhook": {
+                    "config": "captainhook.json"
+                }
+            },
             "autoload": {
                 "psr-4": {
-                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                    "CaptainHook\\App\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -2227,339 +2246,436 @@
             ],
             "authors": [
                 {
-                    "name": "Marco Pivetta",
-                    "email": "ocramius@gmail.com",
-                    "homepage": "https://ocramius.github.io/"
+                    "name": "Sebastian Feldmann",
+                    "email": "sf@sebastian-feldmann.info"
                 }
             ],
-            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
-            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+            "description": "PHP git hook manager",
+            "homepage": "https://github.com/captainhookphp/captainhook",
             "keywords": [
-                "constructor",
-                "instantiate"
+                "commit-msg",
+                "git",
+                "hooks",
+                "post-merge",
+                "pre-commit",
+                "pre-push",
+                "prepare-commit-msg"
             ],
             "support": {
-                "issues": "https://github.com/doctrine/instantiator/issues",
-                "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+                "issues": "https://github.com/captainhookphp/captainhook/issues",
+                "source": "https://github.com/captainhookphp/captainhook/tree/5.9.0"
             },
             "funding": [
                 {
-                    "url": "https://www.doctrine-project.org/sponsorship.html",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://www.patreon.com/phpdoctrine",
-                    "type": "patreon"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
-                    "type": "tidelift"
+                    "url": "https://github.com/sponsors/sebastianfeldmann",
+                    "type": "github"
                 }
             ],
-            "time": "2020-11-10T18:47:58+00:00"
+            "time": "2021-05-05T12:43:16+00:00"
         },
         {
-            "name": "mikey179/vfsstream",
-            "version": "v1.6.8",
+            "name": "composer/package-versions-deprecated",
+            "version": "1.11.99.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/bovigo/vfsStream.git",
-                "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe"
+                "url": "https://github.com/composer/package-versions-deprecated.git",
+                "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe",
-                "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe",
+                "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
+                "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "composer-plugin-api": "^1.1.0 || ^2.0",
+                "php": "^7 || ^8"
+            },
+            "replace": {
+                "ocramius/package-versions": "1.11.99"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.5|^5.0"
+                "composer/composer": "^1.9.3 || ^2.0@dev",
+                "ext-zip": "^1.13",
+                "phpunit/phpunit": "^6.5 || ^7"
             },
-            "type": "library",
+            "type": "composer-plugin",
             "extra": {
+                "class": "PackageVersions\\Installer",
                 "branch-alias": {
-                    "dev-master": "1.6.x-dev"
+                    "dev-master": "1.x-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "org\\bovigo\\vfs\\": "src/main/php"
+                "psr-4": {
+                    "PackageVersions\\": "src/PackageVersions"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Frank Kleine",
-                    "homepage": "http://frankkleine.de/",
-                    "role": "Developer"
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be"
                 }
             ],
-            "description": "Virtual file system to mock the real file system in unit tests.",
-            "homepage": "http://vfs.bovigo.org/",
+            "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
             "support": {
-                "issues": "https://github.com/bovigo/vfsStream/issues",
-                "source": "https://github.com/bovigo/vfsStream/tree/master",
-                "wiki": "https://github.com/bovigo/vfsStream/wiki"
+                "issues": "https://github.com/composer/package-versions-deprecated/issues",
+                "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.1"
             },
-            "time": "2019-10-30T15:31:00+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-11T10:22:58+00:00"
         },
         {
-            "name": "myclabs/deep-copy",
-            "version": "1.10.2",
+            "name": "composer/semver",
+            "version": "3.2.4",
             "source": {
                 "type": "git",
-                "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
+                "url": "https://github.com/composer/semver.git",
+                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
-                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
+                "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1 || ^8.0"
-            },
-            "replace": {
-                "myclabs/deep-copy": "self.version"
+                "php": "^5.3.2 || ^7.0 || ^8.0"
             },
             "require-dev": {
-                "doctrine/collections": "^1.0",
-                "doctrine/common": "^2.6",
-                "phpunit/phpunit": "^7.1"
+                "phpstan/phpstan": "^0.12.54",
+                "symfony/phpunit-bridge": "^4.2 || ^5"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
             "autoload": {
                 "psr-4": {
-                    "DeepCopy\\": "src/DeepCopy/"
-                },
-                "files": [
-                    "src/DeepCopy/deep_copy.php"
-                ]
+                    "Composer\\Semver\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
-            "description": "Create deep copies (clones) of your objects",
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                },
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com",
+                    "homepage": "http://robbast.nl"
+                }
+            ],
+            "description": "Semver library that offers utilities, version constraint parsing and validation.",
             "keywords": [
-                "clone",
-                "copy",
-                "duplicate",
-                "object",
-                "object graph"
+                "semantic",
+                "semver",
+                "validation",
+                "versioning"
             ],
             "support": {
-                "issues": "https://github.com/myclabs/DeepCopy/issues",
-                "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/semver/issues",
+                "source": "https://github.com/composer/semver/tree/3.2.4"
             },
             "funding": [
                 {
-                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-11-13T09:40:50+00:00"
+            "time": "2020-11-13T08:59:24+00:00"
         },
         {
-            "name": "nikic/php-parser",
-            "version": "v4.10.4",
+            "name": "composer/xdebug-handler",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+                "url": "https://github.com/composer/xdebug-handler.git",
+                "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
+                "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
                 "shasum": ""
             },
             "require": {
-                "ext-tokenizer": "*",
-                "php": ">=7.0"
+                "php": "^5.3.2 || ^7.0 || ^8.0",
+                "psr/log": "^1.0"
             },
             "require-dev": {
-                "ircmaxell/php-yacc": "^0.0.7",
-                "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+                "phpstan/phpstan": "^0.12.55",
+                "symfony/phpunit-bridge": "^4.2 || ^5"
             },
-            "bin": [
-                "bin/php-parse"
-            ],
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.9-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
-                    "PhpParser\\": "lib/PhpParser"
+                    "Composer\\XdebugHandler\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Nikita Popov"
+                    "name": "John Stevenson",
+                    "email": "john-stevenson@blueyonder.co.uk"
                 }
             ],
-            "description": "A PHP parser written in PHP",
+            "description": "Restarts a process without Xdebug.",
             "keywords": [
-                "parser",
-                "php"
+                "Xdebug",
+                "performance"
             ],
             "support": {
-                "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/xdebug-handler/issues",
+                "source": "https://github.com/composer/xdebug-handler/tree/2.0.1"
             },
-            "time": "2020-12-20T10:01:03+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-05-05T19:37:51+00:00"
         },
         {
-            "name": "phar-io/manifest",
-            "version": "2.0.1",
+            "name": "danielstjules/stringy",
+            "version": "3.1.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phar-io/manifest.git",
-                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
+                "url": "https://github.com/danielstjules/Stringy.git",
+                "reference": "df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
-                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+                "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e",
+                "reference": "df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e",
                 "shasum": ""
             },
             "require": {
-                "ext-dom": "*",
-                "ext-phar": "*",
-                "ext-xmlwriter": "*",
-                "phar-io/version": "^3.0.1",
-                "php": "^7.2 || ^8.0"
+                "php": ">=5.4.0",
+                "symfony/polyfill-mbstring": "~1.1"
             },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0.x-dev"
-                }
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
             },
+            "type": "library",
             "autoload": {
-                "classmap": [
-                    "src/"
+                "psr-4": {
+                    "Stringy\\": "src/"
+                },
+                "files": [
+                    "src/Create.php"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "name": "Daniel St. Jules",
+                    "email": "danielst.jules@gmail.com",
+                    "homepage": "http://www.danielstjules.com"
                 }
             ],
-            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "description": "A string manipulation library with multibyte support",
+            "homepage": "https://github.com/danielstjules/Stringy",
+            "keywords": [
+                "UTF",
+                "helpers",
+                "manipulation",
+                "methods",
+                "multibyte",
+                "string",
+                "utf-8",
+                "utility",
+                "utils"
+            ],
             "support": {
-                "issues": "https://github.com/phar-io/manifest/issues",
-                "source": "https://github.com/phar-io/manifest/tree/master"
+                "issues": "https://github.com/danielstjules/Stringy/issues",
+                "source": "https://github.com/danielstjules/Stringy"
             },
-            "time": "2020-06-27T14:33:11+00:00"
+            "time": "2017-06-12T01:10:27+00:00"
         },
         {
-            "name": "phar-io/version",
-            "version": "3.1.0",
+            "name": "doctrine/inflector",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phar-io/version.git",
-                "reference": "bae7c545bef187884426f042434e561ab1ddb182"
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
-                "reference": "bae7c545bef187884426f042434e561ab1ddb182",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2 || ^8.0"
             },
+            "require-dev": {
+                "doctrine/coding-standard": "^7.0",
+                "phpstan/phpstan": "^0.11",
+                "phpstan/phpstan-phpunit": "^0.11",
+                "phpstan/phpstan-strict-rules": "^0.11",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+            },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
                 },
                 {
-                    "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
                 },
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
                 }
             ],
-            "description": "Library for handling version information and constraints",
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+            "keywords": [
+                "inflection",
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
             "support": {
-                "issues": "https://github.com/phar-io/version/issues",
-                "source": "https://github.com/phar-io/version/tree/3.1.0"
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.0.x"
             },
-            "time": "2021-02-23T14:00:09+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-29T15:13:26+00:00"
         },
         {
-            "name": "phpdocumentor/reflection-common",
-            "version": "2.2.0",
+            "name": "doctrine/instantiator",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
-                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
-                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
+                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2 || ^8.0"
+                "php": "^7.1 || ^8.0"
             },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-2.x": "2.x-dev"
-                }
+            "require-dev": {
+                "doctrine/coding-standard": "^8.0",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
             },
+            "type": "library",
             "autoload": {
                 "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src/"
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -2568,58 +2684,67 @@
             ],
             "authors": [
                 {
-                    "name": "Jaap van Otterdijk",
-                    "email": "opensource@ijaap.nl"
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "https://ocramius.github.io/"
                 }
             ],
-            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
-            "homepage": "http://www.phpdoc.org",
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
             "keywords": [
-                "FQSEN",
-                "phpDocumentor",
-                "phpdoc",
-                "reflection",
-                "static analysis"
+                "constructor",
+                "instantiate"
             ],
             "support": {
-                "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
-                "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+                "issues": "https://github.com/doctrine/instantiator/issues",
+                "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
             },
-            "time": "2020-06-27T09:03:43+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-10T18:47:58+00:00"
         },
         {
-            "name": "phpdocumentor/reflection-docblock",
-            "version": "5.2.2",
+            "name": "jean85/pretty-package-versions",
+            "version": "1.6.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
+                "url": "https://github.com/Jean85/pretty-package-versions.git",
+                "reference": "1e0104b46f045868f11942aea058cd7186d6c303"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
-                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
+                "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303",
+                "reference": "1e0104b46f045868f11942aea058cd7186d6c303",
                 "shasum": ""
             },
             "require": {
-                "ext-filter": "*",
-                "php": "^7.2 || ^8.0",
-                "phpdocumentor/reflection-common": "^2.2",
-                "phpdocumentor/type-resolver": "^1.3",
-                "webmozart/assert": "^1.9.1"
+                "composer/package-versions-deprecated": "^1.8.0",
+                "php": "^7.0|^8.0"
             },
             "require-dev": {
-                "mockery/mockery": "~1.3.2"
+                "phpunit/phpunit": "^6.0|^8.5|^9.2"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.x-dev"
+                    "dev-master": "1.x-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src"
+                    "Jean85\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -2628,177 +2753,165 @@
             ],
             "authors": [
                 {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
-                },
-                {
-                    "name": "Jaap van Otterdijk",
-                    "email": "account@ijaap.nl"
+                    "name": "Alessandro Lai",
+                    "email": "alessandro.lai85@gmail.com"
                 }
             ],
-            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "description": "A wrapper for ocramius/package-versions to get pretty versions strings",
+            "keywords": [
+                "composer",
+                "package",
+                "release",
+                "versions"
+            ],
             "support": {
-                "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
-                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+                "issues": "https://github.com/Jean85/pretty-package-versions/issues",
+                "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0"
             },
-            "time": "2020-09-03T19:13:55+00:00"
+            "time": "2021-02-04T16:20:16+00:00"
         },
         {
-            "name": "phpdocumentor/type-resolver",
-            "version": "1.4.0",
+            "name": "mikey179/vfsstream",
+            "version": "v1.6.8",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
+                "url": "https://github.com/bovigo/vfsStream.git",
+                "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
-                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+                "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe",
+                "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2 || ^8.0",
-                "phpdocumentor/reflection-common": "^2.0"
+                "php": ">=5.3.0"
             },
             "require-dev": {
-                "ext-tokenizer": "*"
+                "phpunit/phpunit": "^4.5|^5.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-1.x": "1.x-dev"
+                    "dev-master": "1.6.x-dev"
                 }
             },
             "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src"
+                "psr-0": {
+                    "org\\bovigo\\vfs\\": "src/main/php"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "MIT"
+                "BSD-3-Clause"
             ],
             "authors": [
                 {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
+                    "name": "Frank Kleine",
+                    "homepage": "http://frankkleine.de/",
+                    "role": "Developer"
                 }
             ],
-            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+            "description": "Virtual file system to mock the real file system in unit tests.",
+            "homepage": "http://vfs.bovigo.org/",
             "support": {
-                "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
-                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
+                "issues": "https://github.com/bovigo/vfsStream/issues",
+                "source": "https://github.com/bovigo/vfsStream/tree/master",
+                "wiki": "https://github.com/bovigo/vfsStream/wiki"
             },
-            "time": "2020-09-17T18:55:26+00:00"
+            "time": "2019-10-30T15:31:00+00:00"
         },
         {
-            "name": "phpspec/prophecy",
-            "version": "1.13.0",
+            "name": "myclabs/deep-copy",
+            "version": "1.10.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
-                "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
+                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "^1.2",
-                "php": "^7.2 || ~8.0, <8.1",
-                "phpdocumentor/reflection-docblock": "^5.2",
-                "sebastian/comparator": "^3.0 || ^4.0",
-                "sebastian/recursion-context": "^3.0 || ^4.0"
+                "php": "^7.1 || ^8.0"
+            },
+            "replace": {
+                "myclabs/deep-copy": "self.version"
             },
             "require-dev": {
-                "phpspec/phpspec": "^6.0",
-                "phpunit/phpunit": "^8.0 || ^9.0"
+                "doctrine/collections": "^1.0",
+                "doctrine/common": "^2.6",
+                "phpunit/phpunit": "^7.1"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.11.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
-                    "Prophecy\\": "src/Prophecy"
-                }
+                    "DeepCopy\\": "src/DeepCopy/"
+                },
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
-            "authors": [
-                {
-                    "name": "Konstantin Kudryashov",
-                    "email": "ever.zet@gmail.com",
-                    "homepage": "http://everzet.com"
-                },
-                {
-                    "name": "Marcello Duarte",
-                    "email": "marcello.duarte@gmail.com"
-                }
-            ],
-            "description": "Highly opinionated mocking framework for PHP 5.3+",
-            "homepage": "https://github.com/phpspec/prophecy",
+            "description": "Create deep copies (clones) of your objects",
             "keywords": [
-                "Double",
-                "Dummy",
-                "fake",
-                "mock",
-                "spy",
-                "stub"
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
             ],
             "support": {
-                "issues": "https://github.com/phpspec/prophecy/issues",
-                "source": "https://github.com/phpspec/prophecy/tree/1.13.0"
+                "issues": "https://github.com/myclabs/DeepCopy/issues",
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
             },
-            "time": "2021-03-17T13:42:18+00:00"
+            "funding": [
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-13T09:40:50+00:00"
         },
         {
-            "name": "phpunit/php-code-coverage",
-            "version": "9.2.6",
+            "name": "nette/caching",
+            "version": "v3.1.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "f6293e1b30a2354e8428e004689671b83871edde"
+                "url": "https://github.com/nette/caching.git",
+                "reference": "3e771c589dee414724be473c24ad16dae50c1960"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
-                "reference": "f6293e1b30a2354e8428e004689671b83871edde",
+                "url": "https://api.github.com/repos/nette/caching/zipball/3e771c589dee414724be473c24ad16dae50c1960",
+                "reference": "3e771c589dee414724be473c24ad16dae50c1960",
                 "shasum": ""
             },
             "require": {
-                "ext-dom": "*",
-                "ext-libxml": "*",
-                "ext-xmlwriter": "*",
-                "nikic/php-parser": "^4.10.2",
-                "php": ">=7.3",
-                "phpunit/php-file-iterator": "^3.0.3",
-                "phpunit/php-text-template": "^2.0.2",
-                "sebastian/code-unit-reverse-lookup": "^2.0.2",
-                "sebastian/complexity": "^2.0",
-                "sebastian/environment": "^5.1.2",
-                "sebastian/lines-of-code": "^1.0.3",
-                "sebastian/version": "^3.0.1",
-                "theseer/tokenizer": "^1.2.0"
+                "nette/finder": "^2.4 || ^3.0",
+                "nette/utils": "^2.4 || ^3.0",
+                "php": ">=7.2 <8.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "latte/latte": "^2.10",
+                "nette/di": "^v3.0",
+                "nette/tester": "^2.0",
+                "phpstan/phpstan": "^0.12",
+                "tracy/tracy": "^2.4"
             },
             "suggest": {
-                "ext-pcov": "*",
-                "ext-xdebug": "*"
+                "ext-pdo_sqlite": "to use SQLiteStorage or SQLiteJournal"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.2-dev"
+                    "dev-master": "3.1-dev"
                 }
             },
             "autoload": {
@@ -2808,58 +2921,65 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
                 }
             ],
-            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
-            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "description": "⏱ Nette Caching: library with easy-to-use API and many cache backends.",
+            "homepage": "https://nette.org",
             "keywords": [
-                "coverage",
-                "testing",
-                "xunit"
+                "cache",
+                "journal",
+                "memcached",
+                "nette",
+                "sqlite"
             ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
+                "issues": "https://github.com/nette/caching/issues",
+                "source": "https://github.com/nette/caching/tree/v3.1.1"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2021-03-28T07:26:59+00:00"
+            "time": "2021-03-06T14:07:38+00:00"
         },
         {
-            "name": "phpunit/php-file-iterator",
-            "version": "3.0.5",
+            "name": "nette/finder",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
+                "url": "https://github.com/nette/finder.git",
+                "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
-                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
+                "url": "https://api.github.com/repos/nette/finder/zipball/4ad2c298eb8c687dd0e74ae84206a4186eeaed50",
+                "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^2.4 || ^3.0",
+                "php": ">=7.1"
+            },
+            "conflict": {
+                "nette/nette": "<2.2"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "nette/tester": "^2.0",
+                "phpstan/phpstan": "^0.12",
+                "tracy/tracy": "^2.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.0-dev"
+                    "dev-master": "2.5-dev"
                 }
             },
             "autoload": {
@@ -2869,61 +2989,61 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "BSD-3-Clause",
+                "GPL-2.0",
+                "GPL-3.0"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
                 }
             ],
-            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
-            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "description": "🔍 Nette Finder: find files and directories with an intuitive API.",
+            "homepage": "https://nette.org",
             "keywords": [
                 "filesystem",
-                "iterator"
+                "glob",
+                "iterator",
+                "nette"
             ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
-                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+                "issues": "https://github.com/nette/finder/issues",
+                "source": "https://github.com/nette/finder/tree/v2.5.2"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-09-28T05:57:25+00:00"
+            "time": "2020-01-03T20:35:40+00:00"
         },
         {
-            "name": "phpunit/php-invoker",
-            "version": "3.1.1",
+            "name": "nette/neon",
+            "version": "v3.2.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-invoker.git",
-                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+                "url": "https://github.com/nette/neon.git",
+                "reference": "e4ca6f4669121ca6876b1d048c612480e39a28d5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
-                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+                "url": "https://api.github.com/repos/nette/neon/zipball/e4ca6f4669121ca6876b1d048c612480e39a28d5",
+                "reference": "e4ca6f4669121ca6876b1d048c612480e39a28d5",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "ext-json": "*",
+                "php": ">=7.1"
             },
             "require-dev": {
-                "ext-pcntl": "*",
-                "phpunit/phpunit": "^9.3"
-            },
-            "suggest": {
-                "ext-pcntl": "*"
+                "nette/tester": "^2.0",
+                "phpstan/phpstan": "^0.12",
+                "tracy/tracy": "^2.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.1-dev"
+                    "dev-master": "3.2-dev"
                 }
             },
             "autoload": {
@@ -2933,56 +3053,64 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
                 }
             ],
-            "description": "Invoke callables with a timeout",
-            "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+            "description": "🍸 Nette NEON: encodes and decodes NEON file format.",
+            "homepage": "https://ne-on.org",
             "keywords": [
-                "process"
+                "export",
+                "import",
+                "neon",
+                "nette",
+                "yaml"
             ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
-                "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+                "issues": "https://github.com/nette/neon/issues",
+                "source": "https://github.com/nette/neon/tree/v3.2.2"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-09-28T05:58:55+00:00"
+            "time": "2021-02-28T12:30:32+00:00"
         },
         {
-            "name": "phpunit/php-text-template",
-            "version": "2.0.4",
+            "name": "nette/robot-loader",
+            "version": "v3.4.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-text-template.git",
-                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+                "url": "https://github.com/nette/robot-loader.git",
+                "reference": "3973cf3970d1de7b30888fd10b92dac9e0c2fd82"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
-                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+                "url": "https://api.github.com/repos/nette/robot-loader/zipball/3973cf3970d1de7b30888fd10b92dac9e0c2fd82",
+                "reference": "3973cf3970d1de7b30888fd10b92dac9e0c2fd82",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "ext-tokenizer": "*",
+                "nette/finder": "^2.5 || ^3.0",
+                "nette/utils": "^3.0",
+                "php": ">=7.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "nette/tester": "^2.0",
+                "phpstan/phpstan": "^0.12",
+                "tracy/tracy": "^2.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
@@ -2992,56 +3120,73 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
                 }
             ],
-            "description": "Simple template engine.",
-            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "description": "🍀 Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.",
+            "homepage": "https://nette.org",
             "keywords": [
-                "template"
+                "autoload",
+                "class",
+                "interface",
+                "nette",
+                "trait"
             ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
-                "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+                "issues": "https://github.com/nette/robot-loader/issues",
+                "source": "https://github.com/nette/robot-loader/tree/v3.4.0"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-10-26T05:33:50+00:00"
+            "time": "2021-03-07T15:12:01+00:00"
         },
         {
-            "name": "phpunit/php-timer",
-            "version": "5.0.3",
+            "name": "nette/utils",
+            "version": "v3.2.2",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+                "url": "https://github.com/nette/utils.git",
+                "reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
-                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+                "url": "https://api.github.com/repos/nette/utils/zipball/967cfc4f9a1acd5f1058d76715a424c53343c20c",
+                "reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.2 <8.1"
+            },
+            "conflict": {
+                "nette/di": "<3.0.6"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "nette/tester": "~2.0",
+                "phpstan/phpstan": "^0.12",
+                "tracy/tracy": "^2.3"
+            },
+            "suggest": {
+                "ext-gd": "to use Image",
+                "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+                "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+                "ext-json": "to use Nette\\Utils\\Json",
+                "ext-mbstring": "to use Strings::lower() etc...",
+                "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
+                "ext-xml": "to use Strings::length() etc. when mbstring is not available"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.0-dev"
+                    "dev-master": "3.2-dev"
                 }
             },
             "autoload": {
@@ -3051,100 +3196,79 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
                 }
             ],
-            "description": "Utility class for timing",
-            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "description": "🛠  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+            "homepage": "https://nette.org",
             "keywords": [
-                "timer"
+                "array",
+                "core",
+                "datetime",
+                "images",
+                "json",
+                "nette",
+                "paginator",
+                "password",
+                "slugify",
+                "string",
+                "unicode",
+                "utf-8",
+                "utility",
+                "validation"
             ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
-                "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+                "issues": "https://github.com/nette/utils/issues",
+                "source": "https://github.com/nette/utils/tree/v3.2.2"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-10-26T13:16:10+00:00"
+            "time": "2021-03-03T22:53:25+00:00"
         },
         {
-            "name": "phpunit/phpunit",
-            "version": "9.5.4",
+            "name": "nikic/php-parser",
+            "version": "v4.10.4",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "c73c6737305e779771147af66c96ca6a7ed8a741"
+                "url": "https://github.com/nikic/PHP-Parser.git",
+                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741",
-                "reference": "c73c6737305e779771147af66c96ca6a7ed8a741",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "^1.3.1",
-                "ext-dom": "*",
-                "ext-json": "*",
-                "ext-libxml": "*",
-                "ext-mbstring": "*",
-                "ext-xml": "*",
-                "ext-xmlwriter": "*",
-                "myclabs/deep-copy": "^1.10.1",
-                "phar-io/manifest": "^2.0.1",
-                "phar-io/version": "^3.0.2",
-                "php": ">=7.3",
-                "phpspec/prophecy": "^1.12.1",
-                "phpunit/php-code-coverage": "^9.2.3",
-                "phpunit/php-file-iterator": "^3.0.5",
-                "phpunit/php-invoker": "^3.1.1",
-                "phpunit/php-text-template": "^2.0.3",
-                "phpunit/php-timer": "^5.0.2",
-                "sebastian/cli-parser": "^1.0.1",
-                "sebastian/code-unit": "^1.0.6",
-                "sebastian/comparator": "^4.0.5",
-                "sebastian/diff": "^4.0.3",
-                "sebastian/environment": "^5.1.3",
-                "sebastian/exporter": "^4.0.3",
-                "sebastian/global-state": "^5.0.1",
-                "sebastian/object-enumerator": "^4.0.3",
-                "sebastian/resource-operations": "^3.0.3",
-                "sebastian/type": "^2.3",
-                "sebastian/version": "^3.0.2"
+                "ext-tokenizer": "*",
+                "php": ">=7.0"
             },
             "require-dev": {
-                "ext-pdo": "*",
-                "phpspec/prophecy-phpunit": "^2.0.1"
-            },
-            "suggest": {
-                "ext-soap": "*",
-                "ext-xdebug": "*"
+                "ircmaxell/php-yacc": "^0.0.7",
+                "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
             },
             "bin": [
-                "phpunit"
+                "bin/php-parse"
             ],
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.5-dev"
+                    "dev-master": "4.9-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ],
-                "files": [
-                    "src/Framework/Assert/Functions.php"
-                ]
+                "psr-4": {
+                    "PhpParser\\": "lib/PhpParser"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -3152,1053 +3276,5036 @@
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "Nikita Popov"
+                }
+            ],
+            "description": "A PHP parser written in PHP",
+            "keywords": [
+                "parser",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/PHP-Parser/issues",
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+            },
+            "time": "2020-12-20T10:01:03+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-phar": "*",
+                "ext-xmlwriter": "*",
+                "phar-io/version": "^3.0.1",
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "support": {
+                "issues": "https://github.com/phar-io/manifest/issues",
+                "source": "https://github.com/phar-io/manifest/tree/master"
+            },
+            "time": "2020-06-27T14:33:11+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "bae7c545bef187884426f042434e561ab1ddb182"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
+                "reference": "bae7c545bef187884426f042434e561ab1ddb182",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "support": {
+                "issues": "https://github.com/phar-io/version/issues",
+                "source": "https://github.com/phar-io/version/tree/3.1.0"
+            },
+            "time": "2021-02-23T14:00:09+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+            },
+            "time": "2020-06-27T09:03:43+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "5.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
+                "shasum": ""
+            },
+            "require": {
+                "ext-filter": "*",
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.2",
+                "phpdocumentor/type-resolver": "^1.3",
+                "webmozart/assert": "^1.9.1"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                },
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "account@ijaap.nl"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+            },
+            "time": "2020-09-03T19:13:55+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.0"
+            },
+            "require-dev": {
+                "ext-tokenizer": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
+            },
+            "time": "2020-09-17T18:55:26+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
+                "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.2",
+                "php": "^7.2 || ~8.0, <8.1",
+                "phpdocumentor/reflection-docblock": "^5.2",
+                "sebastian/comparator": "^3.0 || ^4.0",
+                "sebastian/recursion-context": "^3.0 || ^4.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^6.0",
+                "phpunit/phpunit": "^8.0 || ^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.11.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Prophecy\\": "src/Prophecy"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "support": {
+                "issues": "https://github.com/phpspec/prophecy/issues",
+                "source": "https://github.com/phpspec/prophecy/tree/1.13.0"
+            },
+            "time": "2021-03-17T13:42:18+00:00"
+        },
+        {
+            "name": "phpstan/phpdoc-parser",
+            "version": "0.5.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpdoc-parser.git",
+                "reference": "e352d065af1ae9b41c12d1dfd309e90f7b1f55c9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e352d065af1ae9b41c12d1dfd309e90f7b1f55c9",
+                "reference": "e352d065af1ae9b41c12d1dfd309e90f7b1f55c9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "phing/phing": "^2.16.3",
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.60",
+                "phpstan/phpstan-strict-rules": "^0.12.5",
+                "phpunit/phpunit": "^7.5.20",
+                "symfony/process": "^5.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\PhpDocParser\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPDoc parser with support for nullable, intersection and generic types",
+            "support": {
+                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/0.5.4"
+            },
+            "time": "2021-04-03T14:46:19+00:00"
+        },
+        {
+            "name": "phpstan/phpstan",
+            "version": "0.12.85",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpstan.git",
+                "reference": "20e6333c0067875ad7697cd8acdf245c6ef69d03"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/20e6333c0067875ad7697cd8acdf245c6ef69d03",
+                "reference": "20e6333c0067875ad7697cd8acdf245c6ef69d03",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1|^8.0"
+            },
+            "conflict": {
+                "phpstan/phpstan-shim": "*"
+            },
+            "bin": [
+                "phpstan",
+                "phpstan.phar"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.12-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPStan - PHP Static Analysis Tool",
+            "support": {
+                "issues": "https://github.com/phpstan/phpstan/issues",
+                "source": "https://github.com/phpstan/phpstan/tree/0.12.85"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ondrejmirtes",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/phpstan",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-27T14:13:16+00:00"
+        },
+        {
+            "name": "phpstan/phpstan-phpunit",
+            "version": "0.12.18",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpstan-phpunit.git",
+                "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72",
+                "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0",
+                "phpstan/phpstan": "^0.12.60"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<7.0"
+            },
+            "require-dev": {
+                "phing/phing": "^2.16.3",
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/phpstan-strict-rules": "^0.12.6",
+                "phpunit/phpunit": "^7.5.20"
+            },
+            "type": "phpstan-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.12-dev"
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon",
+                        "rules.neon"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPUnit extensions and rules for PHPStan",
+            "support": {
+                "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+                "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.18"
+            },
+            "time": "2021-03-06T11:51:27+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "9.2.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-xmlwriter": "*",
+                "nikic/php-parser": "^4.10.2",
+                "php": ">=7.3",
+                "phpunit/php-file-iterator": "^3.0.3",
+                "phpunit/php-text-template": "^2.0.2",
+                "sebastian/code-unit-reverse-lookup": "^2.0.2",
+                "sebastian/complexity": "^2.0",
+                "sebastian/environment": "^5.1.2",
+                "sebastian/lines-of-code": "^1.0.3",
+                "sebastian/version": "^3.0.1",
+                "theseer/tokenizer": "^1.2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-pcov": "*",
+                "ext-xdebug": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.2-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-03-28T07:26:59+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "3.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8",
+                "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:57:25+00:00"
+        },
+        {
+            "name": "phpunit/php-invoker",
+            "version": "3.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-invoker.git",
+                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "ext-pcntl": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-pcntl": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Invoke callables with a timeout",
+            "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+            "keywords": [
+                "process"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+                "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:58:55+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+                "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T05:33:50+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "5.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+                "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:16:10+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "9.5.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "c73c6737305e779771147af66c96ca6a7ed8a741"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741",
+                "reference": "c73c6737305e779771147af66c96ca6a7ed8a741",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.3.1",
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "ext-xmlwriter": "*",
+                "myclabs/deep-copy": "^1.10.1",
+                "phar-io/manifest": "^2.0.1",
+                "phar-io/version": "^3.0.2",
+                "php": ">=7.3",
+                "phpspec/prophecy": "^1.12.1",
+                "phpunit/php-code-coverage": "^9.2.3",
+                "phpunit/php-file-iterator": "^3.0.5",
+                "phpunit/php-invoker": "^3.1.1",
+                "phpunit/php-text-template": "^2.0.3",
+                "phpunit/php-timer": "^5.0.2",
+                "sebastian/cli-parser": "^1.0.1",
+                "sebastian/code-unit": "^1.0.6",
+                "sebastian/comparator": "^4.0.5",
+                "sebastian/diff": "^4.0.3",
+                "sebastian/environment": "^5.1.3",
+                "sebastian/exporter": "^4.0.3",
+                "sebastian/global-state": "^5.0.1",
+                "sebastian/object-enumerator": "^4.0.3",
+                "sebastian/resource-operations": "^3.0.3",
+                "sebastian/type": "^2.3",
+                "sebastian/version": "^3.0.2"
+            },
+            "require-dev": {
+                "ext-pdo": "*",
+                "phpspec/prophecy-phpunit": "^2.0.1"
+            },
+            "suggest": {
+                "ext-soap": "*",
+                "ext-xdebug": "*"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.5-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ],
+                "files": [
+                    "src/Framework/Assert/Functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4"
+            },
+            "funding": [
+                {
+                    "url": "https://phpunit.de/donate.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-03-23T07:16:29+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
+                "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/1.1.1"
+            },
+            "time": "2021-03-05T17:36:06+00:00"
+        },
+        {
+            "name": "psr/event-dispatcher",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/event-dispatcher.git",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\EventDispatcher\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Standard interfaces for event handling.",
+            "keywords": [
+                "events",
+                "psr",
+                "psr-14"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/event-dispatcher/issues",
+                "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+            },
+            "time": "2019-01-08T18:20:26+00:00"
+        },
+        {
+            "name": "rector/rector",
+            "version": "0.10.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector.git",
+                "reference": "efb19e74e60347b1280369e19b26b29ac22199d5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector/zipball/efb19e74e60347b1280369e19b26b29ac22199d5",
+                "reference": "efb19e74e60347b1280369e19b26b29ac22199d5",
+                "shasum": ""
+            },
+            "require": {
+                "composer/semver": "^3.2",
+                "composer/xdebug-handler": "^2.0",
+                "danielstjules/stringy": "^3.1",
+                "doctrine/inflector": "^2.0",
+                "ext-dom": "*",
+                "ext-json": "*",
+                "jean85/pretty-package-versions": "^1.6",
+                "nette/caching": "^3.1",
+                "nette/robot-loader": "^3.4",
+                "nette/utils": "^3.2",
+                "nikic/php-parser": "4.10.4",
+                "php": "^7.3|^8.0",
+                "phpstan/phpdoc-parser": "^0.5.4",
+                "phpstan/phpstan": "0.12.85",
+                "phpstan/phpstan-phpunit": "^0.12.18",
+                "rector/rector-cakephp": "^0.10.4",
+                "rector/rector-doctrine": "^0.10.6",
+                "rector/rector-installer": "^0.10.0",
+                "rector/rector-laravel": "^0.10.2",
+                "rector/rector-nette": "^0.10.8",
+                "rector/rector-phpunit": "^0.10.8",
+                "rector/rector-symfony": "^0.10.5",
+                "sebastian/diff": "^4.0.4",
+                "symfony/console": "^4.4.8|^5.1",
+                "symfony/dependency-injection": "^5.1",
+                "symfony/finder": "^4.4.8|^5.1",
+                "symfony/http-kernel": "^4.4.8|^5.1",
+                "symfony/process": "^4.4.8|^5.1",
+                "symplify/astral": "^9.3",
+                "symplify/autowire-array-parameter": "^9.3",
+                "symplify/console-color-diff": "^9.3",
+                "symplify/package-builder": "^9.3",
+                "symplify/rule-doc-generator-contracts": "^9.3",
+                "symplify/set-config-resolver": "^9.3",
+                "symplify/simple-php-doc-parser": "^9.3",
+                "symplify/skipper": "^9.3",
+                "symplify/smart-file-system": "^9.3",
+                "symplify/symfony-php-config": "^9.3",
+                "tracy/tracy": "^2.8",
+                "webmozart/assert": "^1.10"
+            },
+            "replace": {
+                "rector/rector-prefixed": "self.version"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.0",
+                "nette/application": "^3.0.7",
+                "nette/di": "^3.0",
+                "nette/forms": "^3.0",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan-nette": "^0.12.16",
+                "phpunit/phpunit": "^9.5",
+                "rector/rector-generator": "^0.1.7",
+                "rector/rector-phpstan-rules": "^0.1",
+                "symplify/coding-standard": "^9.3",
+                "symplify/easy-ci": "^9.3",
+                "symplify/easy-coding-standard": "^9.3",
+                "symplify/easy-testing": "^9.3",
+                "symplify/phpstan-extensions": "^9.3",
+                "symplify/phpstan-rules": "^9.3",
+                "symplify/rule-doc-generator": "^9.3"
+            },
+            "bin": [
+                "bin/rector"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\": [
+                        "packages",
+                        "rules"
+                    ],
+                    "Rector\\Core\\": "src",
+                    "Rector\\Compiler\\": "utils/compiler/src"
+                },
+                "files": [
+                    "src/functions/node_helper.php",
+                    "src/constants.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Tomas Votruba",
+                    "email": "tomas.vot@gmail.com",
+                    "homepage": "https://tomasvotruba.com"
+                },
+                {
+                    "name": "Jan Mikes",
+                    "email": "j.mikes@me.com",
+                    "homepage": "https://janmikes.cz"
+                }
+            ],
+            "description": "Instant upgrade and refactoring of your PHP code",
+            "homepage": "https://getrector.org",
+            "keywords": [
+                "ast",
+                "automated refactoring",
+                "instant refactoring",
+                "instant upgrades"
+            ],
+            "support": {
+                "issues": "https://github.com/rectorphp/rector/issues",
+                "source": "https://github.com/rectorphp/rector/tree/0.10.19"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/tomasvotruba",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-05-04T18:40:08+00:00"
+        },
+        {
+            "name": "rector/rector-cakephp",
+            "version": "0.10.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-cakephp.git",
+                "reference": "5155ac30d2cd04144690c118a030af0a4a1e68f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-cakephp/zipball/5155ac30d2cd04144690c118a030af0a4a1e68f4",
+                "reference": "5155ac30d2cd04144690c118a030af0a4a1e68f4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-xml": "*",
+                "php": ">=7.3",
+                "rector/rector": "^0.10.5"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\CakePHP\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for CakePHP",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-cakephp/issues",
+                "source": "https://github.com/rectorphp/rector-cakephp/tree/0.10.4"
+            },
+            "time": "2021-04-15T22:27:55+00:00"
+        },
+        {
+            "name": "rector/rector-doctrine",
+            "version": "0.10.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-doctrine.git",
+                "reference": "52dbdf1d60c522c1c40142a194960610db451f91"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-doctrine/zipball/52dbdf1d60c522c1c40142a194960610db451f91",
+                "reference": "52dbdf1d60c522c1c40142a194960610db451f91",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "rector/rector": "^0.10.10"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2",
+                "tracy/tracy": "^2.8"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\Doctrine\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for Doctrine",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-doctrine/issues",
+                "source": "https://github.com/rectorphp/rector-doctrine/tree/0.10.6"
+            },
+            "time": "2021-04-24T12:17:00+00:00"
+        },
+        {
+            "name": "rector/rector-installer",
+            "version": "0.10.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-installer.git",
+                "reference": "2e8df01aac6a3dedd113ba57ea6f129b6a9872c9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-installer/zipball/2e8df01aac6a3dedd113ba57ea6f129b6a9872c9",
+                "reference": "2e8df01aac6a3dedd113ba57ea6f129b6a9872c9",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^1.1 || ^2.0",
+                "php": "^7.3 || ^8.0"
+            },
+            "require-dev": {
+                "composer/composer": "^2.0",
+                "jangregor/phpstan-prophecy": "^0.8.1",
+                "phpspec/prophecy-phpunit": "^2.0",
+                "phpstan/extension-installer": "^1.1",
+                "phpunit/phpunit": "^9.5",
+                "rector/rector-phpstan-rules": "^0.1.0",
+                "symplify/easy-coding-standard": "^9.2.24",
+                "symplify/phpstan-extensions": "^9.2.24",
+                "symplify/phpstan-rules": "^9.2.24"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "Rector\\RectorInstaller\\Plugin"
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\RectorInstaller\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Composer plugin for automatic installation of rector extensions",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-installer/issues",
+                "source": "https://github.com/rectorphp/rector-installer/tree/0.10.1"
+            },
+            "time": "2021-04-26T14:25:12+00:00"
+        },
+        {
+            "name": "rector/rector-laravel",
+            "version": "0.10.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-laravel.git",
+                "reference": "d4ccd50d7975bea9a0ce961bbaded385b5ede739"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-laravel/zipball/d4ccd50d7975bea9a0ce961bbaded385b5ede739",
+                "reference": "d4ccd50d7975bea9a0ce961bbaded385b5ede739",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "rector/rector": "^0.10.5"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2",
+                "tracy/tracy": "^2.8"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\Laravel\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for Laravel Framework",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-laravel/issues",
+                "source": "https://github.com/rectorphp/rector-laravel/tree/0.10.2"
+            },
+            "time": "2021-04-15T22:21:20+00:00"
+        },
+        {
+            "name": "rector/rector-nette",
+            "version": "0.10.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-nette.git",
+                "reference": "19c85c870f9a7a90d1f2e9be9b3e048b4db69697"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-nette/zipball/19c85c870f9a7a90d1f2e9be9b3e048b4db69697",
+                "reference": "19c85c870f9a7a90d1f2e9be9b3e048b4db69697",
+                "shasum": ""
+            },
+            "require": {
+                "ext-xml": "*",
+                "php": ">=7.3",
+                "rector/rector": "^0.10.12"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "nette/application": "^3.0.7",
+                "nette/di": "^3.0",
+                "nette/forms": "3.0.*",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan-nette": "^0.12.16",
+                "phpunit/phpunit": "^9.5",
+                "rector/rector-phpstan-rules": "^0.1",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\Nette\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for Nette Framework",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-nette/issues",
+                "source": "https://github.com/rectorphp/rector-nette/tree/0.10.9"
+            },
+            "time": "2021-04-26T10:35:59+00:00"
+        },
+        {
+            "name": "rector/rector-phpunit",
+            "version": "0.10.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-phpunit.git",
+                "reference": "2b272b61cacf29bf0e448719165c8376cd41872e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-phpunit/zipball/2b272b61cacf29bf0e448719165c8376cd41872e",
+                "reference": "2b272b61cacf29bf0e448719165c8376cd41872e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "rector/rector": "^0.10.10"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2",
+                "tracy/tracy": "^2.8"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\PHPUnit\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for PHPUnit",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-phpunit/issues",
+                "source": "https://github.com/rectorphp/rector-phpunit/tree/0.10.8"
+            },
+            "time": "2021-04-24T12:18:06+00:00"
+        },
+        {
+            "name": "rector/rector-symfony",
+            "version": "0.10.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rectorphp/rector-symfony.git",
+                "reference": "583e77ad2fc8c7740418d55884beb3d18822e115"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rectorphp/rector-symfony/zipball/583e77ad2fc8c7740418d55884beb3d18822e115",
+                "reference": "583e77ad2fc8c7740418d55884beb3d18822e115",
+                "shasum": ""
+            },
+            "require": {
+                "ext-xml": "*",
+                "php": ">=7.3",
+                "rector/rector": "^0.10.12"
+            },
+            "conflict": {
+                "rector/rector": "<=0.10.3"
+            },
+            "require-dev": {
+                "phpstan/extension-installer": "^1.1",
+                "phpunit/phpunit": "^9.5",
+                "rector/rector-phpstan-rules": "dev-main",
+                "symfony/security-core": "^5.2",
+                "symfony/security-http": "^5.2",
+                "symplify/easy-coding-standard": "^9.2",
+                "symplify/phpstan-extensions": "^9.2",
+                "symplify/phpstan-rules": "^9.2",
+                "symplify/rule-doc-generator": "^9.2"
+            },
+            "type": "rector-extension",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.10-dev"
+                },
+                "rector": {
+                    "includes": [
+                        "config/config.php"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Rector\\Symfony\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Rector upgrades rules for Symfony Framework",
+            "support": {
+                "issues": "https://github.com/rectorphp/rector-symfony/issues",
+                "source": "https://github.com/rectorphp/rector-symfony/tree/0.10.6"
+            },
+            "time": "2021-04-26T10:06:34+00:00"
+        },
+        {
+            "name": "sebastian/cli-parser",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/cli-parser.git",
+                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for parsing CLI options",
+            "homepage": "https://github.com/sebastianbergmann/cli-parser",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+                "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:08:49+00:00"
+        },
+        {
+            "name": "sebastian/code-unit",
+            "version": "1.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit.git",
+                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Collection of value objects that represent the PHP code units",
+            "homepage": "https://github.com/sebastianbergmann/code-unit",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+                "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:08:54+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "2.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Looks up which function or method a line of code belongs to",
+            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:30:19+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "4.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
+                "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/diff": "^4.0",
+                "sebastian/exporter": "^4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/comparator/issues",
+                "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T15:49:45+00:00"
+        },
+        {
+            "name": "sebastian/complexity",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/complexity.git",
+                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^4.7",
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for calculating the complexity of PHP code units",
+            "homepage": "https://github.com/sebastianbergmann/complexity",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/complexity/issues",
+                "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T15:52:27+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3",
+                "symfony/process": "^4.2 || ^5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff",
+                "udiff",
+                "unidiff",
+                "unified diff"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/diff/issues",
+                "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:10:38+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "5.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "388b6ced16caa751030f6a69e588299fa09200ac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
+                "reference": "388b6ced16caa751030f6a69e588299fa09200ac",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-posix": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/environment/issues",
+                "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:52:38+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "4.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/exporter/issues",
+                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:24:23+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "5.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
+                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/object-reflector": "^2.0",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "ext-dom": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/global-state/issues",
+                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T15:55:19+00:00"
+        },
+        {
+            "name": "sebastian/lines-of-code",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^4.6",
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for counting the lines of code in PHP source code",
+            "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-28T06:42:11+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/object-reflector": "^2.0",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+                "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:12:34+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+                "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:14:26+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+                "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:17:30+00:00"
+        },
+        {
+            "name": "sebastian/resource-operations",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides a list of PHP built-in functions that operate on resources",
+            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+                "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:45:17+00:00"
+        },
+        {
+            "name": "sebastian/type",
+            "version": "2.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/type.git",
+                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
+                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Collection of value objects that represent the types of the PHP type system",
+            "homepage": "https://github.com/sebastianbergmann/type",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/type/issues",
+                "source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:18:59+00:00"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "c6c1022351a901512170118436c764e473f6de8c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+                "reference": "c6c1022351a901512170118436c764e473f6de8c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/version/issues",
+                "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:39:44+00:00"
+        },
+        {
+            "name": "sebastianfeldmann/camino",
+            "version": "0.9.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianfeldmann/camino.git",
+                "reference": "3b611368e22e8565c3a6504613136402ed9e6f69"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianfeldmann/camino/zipball/3b611368e22e8565c3a6504613136402ed9e6f69",
+                "reference": "3b611368e22e8565c3a6504613136402ed9e6f69",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SebastianFeldmann\\Camino\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Feldmann",
+                    "email": "sf@sebastian-feldmann.info"
+                }
+            ],
+            "description": "Path management the OO way",
+            "homepage": "https://github.com/sebastianfeldmann/camino",
+            "keywords": [
+                "file system",
+                "path"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianfeldmann/camino/issues",
+                "source": "https://github.com/sebastianfeldmann/camino/tree/0.9.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianfeldmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-08T09:25:24+00:00"
+        },
+        {
+            "name": "sebastianfeldmann/cli",
+            "version": "3.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianfeldmann/cli.git",
+                "reference": "490a6ba01d62e56d597b4d3377024feb87871f6f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianfeldmann/cli/zipball/490a6ba01d62e56d597b4d3377024feb87871f6f",
+                "reference": "490a6ba01d62e56d597b4d3377024feb87871f6f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "require-dev": {
+                "symfony/process": "^4.3 | ^5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SebastianFeldmann\\Cli\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Feldmann",
+                    "email": "sf@sebastian-feldmann.info"
+                }
+            ],
+            "description": "PHP cli helper classes",
+            "homepage": "https://github.com/sebastianfeldmann/cli",
+            "keywords": [
+                "cli"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianfeldmann/cli/issues",
+                "source": "https://github.com/sebastianfeldmann/cli/tree/3.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianfeldmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-08T09:20:06+00:00"
+        },
+        {
+            "name": "sebastianfeldmann/git",
+            "version": "3.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianfeldmann/git.git",
+                "reference": "ca827dc741a90043ee46e59a96b1ca88b9ed5264"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/ca827dc741a90043ee46e59a96b1ca88b9ed5264",
+                "reference": "ca827dc741a90043ee46e59a96b1ca88b9ed5264",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-xml": "*",
+                "php": ">=7.2",
+                "sebastianfeldmann/cli": "^3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SebastianFeldmann\\Git\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Feldmann",
+                    "email": "sf@sebastian-feldmann.info"
+                }
+            ],
+            "description": "PHP git wrapper",
+            "homepage": "https://github.com/sebastianfeldmann/git",
+            "keywords": [
+                "git"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianfeldmann/git/issues",
+                "source": "https://github.com/sebastianfeldmann/git/tree/3.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianfeldmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-04-10T08:31:02+00:00"
+        },
+        {
+            "name": "squizlabs/php_codesniffer",
+            "version": "3.5.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
+                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-simplexml": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+            },
+            "bin": [
+                "bin/phpcs",
+                "bin/phpcbf"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Sherwood",
+                    "role": "lead"
+                }
+            ],
+            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+            "keywords": [
+                "phpcs",
+                "standards"
+            ],
+            "support": {
+                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+            },
+            "time": "2020-10-23T02:01:07+00:00"
+        },
+        {
+            "name": "symfony/config",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/config.git",
+                "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/config/zipball/3817662ada105c8c4d1afdb4ec003003efd1d8d8",
+                "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/filesystem": "^4.4|^5.0",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "symfony/finder": "<4.4"
+            },
+            "require-dev": {
+                "symfony/event-dispatcher": "^4.4|^5.0",
+                "symfony/finder": "^4.4|^5.0",
+                "symfony/messenger": "^4.4|^5.0",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/yaml": "^4.4|^5.0"
+            },
+            "suggest": {
+                "symfony/yaml": "To use the yaml reference dumper"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Config\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/config/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-07T16:07:52+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/90374b8ed059325b49a29b55b3f8bb4062c87629",
+                "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php73": "^1.8",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/string": "^5.1"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<4.4",
+                "symfony/dotenv": "<5.1",
+                "symfony/event-dispatcher": "<4.4",
+                "symfony/lock": "<4.4",
+                "symfony/process": "<4.4"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "^4.4|^5.0",
+                "symfony/dependency-injection": "^4.4|^5.0",
+                "symfony/event-dispatcher": "^4.4|^5.0",
+                "symfony/lock": "^4.4|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "symfony/var-dumper": "^4.4|^5.0"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/lock": "",
+                "symfony/process": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Eases the creation of beautiful and testable command line interfaces",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "cli",
+                "command line",
+                "console",
+                "terminal"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/console/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-19T14:07:32+00:00"
+        },
+        {
+            "name": "symfony/dependency-injection",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/dependency-injection.git",
+                "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6ca378b99e3c9ba6127eb43b68389fb2b7348577",
+                "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/container": "^1.0",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/service-contracts": "^1.1.6|^2"
+            },
+            "conflict": {
+                "symfony/config": "<5.1",
+                "symfony/finder": "<4.4",
+                "symfony/proxy-manager-bridge": "<4.4",
+                "symfony/yaml": "<4.4"
+            },
+            "provide": {
+                "psr/container-implementation": "1.0",
+                "symfony/service-implementation": "1.0|2.0"
+            },
+            "require-dev": {
+                "symfony/config": "^5.1",
+                "symfony/expression-language": "^4.4|^5.0",
+                "symfony/yaml": "^4.4|^5.0"
+            },
+            "suggest": {
+                "symfony/config": "",
+                "symfony/expression-language": "For using expressions in service container configuration",
+                "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
+                "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\DependencyInjection\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows you to standardize and centralize the way objects are constructed in your application",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/dependency-injection/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-24T14:32:26+00:00"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
+                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.4-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-03-23T23:28:01+00:00"
+        },
+        {
+            "name": "symfony/error-handler",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/error-handler.git",
+                "reference": "ea3ddbf67615e883ca7c33a4de61213789846782"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/ea3ddbf67615e883ca7c33a4de61213789846782",
+                "reference": "ea3ddbf67615e883ca7c33a4de61213789846782",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/log": "^1.0",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/var-dumper": "^4.4|^5.0"
+            },
+            "require-dev": {
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/http-kernel": "^4.4|^5.0",
+                "symfony/serializer": "^4.4|^5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\ErrorHandler\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to manage errors and ease debugging PHP code",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/error-handler/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-07T15:57:33+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v5.2.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "d08d6ec121a425897951900ab692b612a61d6240"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240",
+                "reference": "d08d6ec121a425897951900ab692b612a61d6240",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/event-dispatcher-contracts": "^2",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<4.4"
+            },
+            "provide": {
+                "psr/event-dispatcher-implementation": "1.0",
+                "symfony/event-dispatcher-implementation": "2.0"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "^4.4|^5.0",
+                "symfony/dependency-injection": "^4.4|^5.0",
+                "symfony/error-handler": "^4.4|^5.0",
+                "symfony/expression-language": "^4.4|^5.0",
+                "symfony/http-foundation": "^4.4|^5.0",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/stopwatch": "^4.4|^5.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-02-18T17:12:37+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher-contracts",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+                "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11",
+                "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/event-dispatcher": "^1"
+            },
+            "suggest": {
+                "symfony/event-dispatcher-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.4-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to dispatching event",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-03-23T23:28:01+00:00"
+        },
+        {
+            "name": "symfony/filesystem",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/filesystem.git",
+                "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/056e92acc21d977c37e6ea8e97374b2a6c8551b0",
+                "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Filesystem\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides basic utilities for the filesystem",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/filesystem/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-01T10:42:13+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v5.2.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "0d639a0943822626290d169965804f79400e6a04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04",
+                "reference": "0d639a0943822626290d169965804f79400e6a04",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Finds files and directories via an intuitive fluent interface",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/finder/tree/v5.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-02-15T18:55:04+00:00"
+        },
+        {
+            "name": "symfony/http-client-contracts",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-client-contracts.git",
+                "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4",
+                "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "suggest": {
+                "symfony/http-client-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.4-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\HttpClient\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to HTTP clients",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-04-11T23:07:08+00:00"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f",
+                "reference": "a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/polyfill-mbstring": "~1.1",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "require-dev": {
+                "predis/predis": "~1.0",
+                "symfony/cache": "^4.4|^5.0",
+                "symfony/expression-language": "^4.4|^5.0",
+                "symfony/mime": "^4.4|^5.0"
+            },
+            "suggest": {
+                "symfony/mime": "To use the file extension guesser"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Defines an object-oriented layer for the HTTP specification",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-foundation/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-05-01T13:46:24+00:00"
+        },
+        {
+            "name": "symfony/http-kernel",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-kernel.git",
+                "reference": "1e9f6879f070f718e0055fbac232a56f67b8b6bd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1e9f6879f070f718e0055fbac232a56f67b8b6bd",
+                "reference": "1e9f6879f070f718e0055fbac232a56f67b8b6bd",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/log": "~1.0",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/error-handler": "^4.4|^5.0",
+                "symfony/event-dispatcher": "^5.0",
+                "symfony/http-client-contracts": "^1.1|^2",
+                "symfony/http-foundation": "^4.4|^5.0",
+                "symfony/polyfill-ctype": "^1.8",
+                "symfony/polyfill-php73": "^1.9",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "symfony/browser-kit": "<4.4",
+                "symfony/cache": "<5.0",
+                "symfony/config": "<5.0",
+                "symfony/console": "<4.4",
+                "symfony/dependency-injection": "<5.1.8",
+                "symfony/doctrine-bridge": "<5.0",
+                "symfony/form": "<5.0",
+                "symfony/http-client": "<5.0",
+                "symfony/mailer": "<5.0",
+                "symfony/messenger": "<5.0",
+                "symfony/translation": "<5.0",
+                "symfony/twig-bridge": "<5.0",
+                "symfony/validator": "<5.0",
+                "twig/twig": "<2.13"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0"
+            },
+            "require-dev": {
+                "psr/cache": "^1.0|^2.0|^3.0",
+                "symfony/browser-kit": "^4.4|^5.0",
+                "symfony/config": "^5.0",
+                "symfony/console": "^4.4|^5.0",
+                "symfony/css-selector": "^4.4|^5.0",
+                "symfony/dependency-injection": "^5.1.8",
+                "symfony/dom-crawler": "^4.4|^5.0",
+                "symfony/expression-language": "^4.4|^5.0",
+                "symfony/finder": "^4.4|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "symfony/routing": "^4.4|^5.0",
+                "symfony/stopwatch": "^4.4|^5.0",
+                "symfony/translation": "^4.4|^5.0",
+                "symfony/translation-contracts": "^1.1|^2",
+                "twig/twig": "^2.13|^3.0.4"
+            },
+            "suggest": {
+                "symfony/browser-kit": "",
+                "symfony/config": "",
+                "symfony/console": "",
+                "symfony/dependency-injection": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpKernel\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides a structured process for converting a Request into a Response",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-kernel/tree/v5.2.7"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-05-01T14:53:15+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-grapheme",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+                "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170",
+                "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's grapheme_* functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "grapheme",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-22T09:19:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
+                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-22T09:19:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php73",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php73.git",
+                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php73\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v5.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e",
+                "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "The PHP Unit Testing framework.",
-            "homepage": "https://phpunit.de/",
-            "keywords": [
-                "phpunit",
-                "testing",
-                "xunit"
-            ],
+            "description": "Executes commands in sub-processes",
+            "homepage": "https://symfony.com",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4"
+                "source": "https://github.com/symfony/process/tree/v5.3.0-BETA1"
             },
             "funding": [
                 {
-                    "url": "https://phpunit.de/donate.html",
+                    "url": "https://symfony.com/sponsor",
                     "type": "custom"
                 },
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://github.com/fabpot",
                     "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
                 }
             ],
-            "time": "2021-03-23T07:16:29+00:00"
+            "time": "2021-04-08T10:27:02+00:00"
         },
         {
-            "name": "sebastian/cli-parser",
-            "version": "1.0.1",
+            "name": "symfony/service-contracts",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/cli-parser.git",
-                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
-                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.2.5",
+                "psr/container": "^1.1"
             },
-            "require-dev": {
-                "phpunit/phpunit": "^9.3"
+            "suggest": {
+                "symfony/service-implementation": ""
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0-dev"
+                    "dev-main": "2.4-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Library for parsing CLI options",
-            "homepage": "https://github.com/sebastianbergmann/cli-parser",
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
-                "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+                "source": "https://github.com/symfony/service-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
                     "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
                 }
             ],
-            "time": "2020-09-28T06:08:49+00:00"
+            "time": "2021-04-01T10:43:52+00:00"
         },
         {
-            "name": "sebastian/code-unit",
-            "version": "1.0.8",
+            "name": "symfony/string",
+            "version": "v5.2.6",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/code-unit.git",
-                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+                "url": "https://github.com/symfony/string.git",
+                "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
-                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+                "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572",
+                "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.2.5",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-intl-grapheme": "~1.0",
+                "symfony/polyfill-intl-normalizer": "~1.0",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php80": "~1.15"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "symfony/error-handler": "^4.4|^5.0",
+                "symfony/http-client": "^4.4|^5.0",
+                "symfony/translation-contracts": "^1.1|^2",
+                "symfony/var-exporter": "^4.4|^5.0"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                }
-            },
             "autoload": {
-                "classmap": [
-                    "src/"
+                "psr-4": {
+                    "Symfony\\Component\\String\\": ""
+                },
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "exclude-from-classmap": [
+                    "/Tests/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Collection of value objects that represent the PHP code units",
-            "homepage": "https://github.com/sebastianbergmann/code-unit",
+            "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "grapheme",
+                "i18n",
+                "string",
+                "unicode",
+                "utf-8",
+                "utf8"
+            ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/code-unit/issues",
-                "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+                "source": "https://github.com/symfony/string/tree/v5.2.6"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
                     "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
                 }
             ],
-            "time": "2020-10-26T13:08:54+00:00"
+            "time": "2021-03-17T17:12:15+00:00"
         },
         {
-            "name": "sebastian/code-unit-reverse-lookup",
-            "version": "2.0.3",
+            "name": "symfony/var-dumper",
+            "version": "v5.2.7",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "27cb9f7cfa3853c736425c7233a8f68814b19636"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
-                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/27cb9f7cfa3853c736425c7233a8f68814b19636",
+                "reference": "27cb9f7cfa3853c736425c7233a8f68814b19636",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.2.5",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<5.4.3",
+                "symfony/console": "<4.4"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "ext-iconv": "*",
+                "symfony/console": "^4.4|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "twig/twig": "^2.13|^3.0.4"
             },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
+            "suggest": {
+                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+                "ext-intl": "To show region name in time zone dump",
+                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
             },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
             "autoload": {
-                "classmap": [
-                    "src/"
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Looks up which function or method a line of code belongs to",
-            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
             "support": {
-                "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
-                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+                "source": "https://github.com/symfony/var-dumper/tree/v5.2.7"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
                     "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
                 }
             ],
-            "time": "2020-09-28T05:30:19+00:00"
+            "time": "2021-04-19T14:07:32+00:00"
         },
         {
-            "name": "sebastian/comparator",
-            "version": "4.0.6",
+            "name": "symplify/astral",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
+                "url": "https://github.com/symplify/astral.git",
+                "reference": "4347ac8b82c7abc46007850147bdd48f2dd9f511"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
-                "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
+                "url": "https://api.github.com/repos/symplify/astral/zipball/4347ac8b82c7abc46007850147bdd48f2dd9f511",
+                "reference": "4347ac8b82c7abc46007850147bdd48f2dd9f511",
                 "shasum": ""
             },
             "require": {
+                "nette/utils": "^3.2",
+                "nikic/php-parser": "4.10.4",
                 "php": ">=7.3",
-                "sebastian/diff": "^4.0",
-                "sebastian/exporter": "^4.0"
+                "symfony/dependency-injection": "^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/autowire-array-parameter": "^9.3.1",
+                "symplify/package-builder": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-testing": "^9.3.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\Astral\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@2bepublished.at"
-                }
-            ],
-            "description": "Provides the functionality to compare PHP values for equality",
-            "homepage": "https://github.com/sebastianbergmann/comparator",
-            "keywords": [
-                "comparator",
-                "compare",
-                "equality"
+                "MIT"
             ],
+            "description": "Toolking for smart daily work with AST",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/comparator/issues",
-                "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+                "source": "https://github.com/symplify/astral/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T15:49:45+00:00"
+            "time": "2021-05-04T18:51:08+00:00"
         },
         {
-            "name": "sebastian/complexity",
-            "version": "2.0.2",
+            "name": "symplify/autowire-array-parameter",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/complexity.git",
-                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+                "url": "https://github.com/symplify/autowire-array-parameter.git",
+                "reference": "130fff4f734e752dee5d14da26038e9c213d1381"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
-                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+                "url": "https://api.github.com/repos/symplify/autowire-array-parameter/zipball/130fff4f734e752dee5d14da26038e9c213d1381",
+                "reference": "130fff4f734e752dee5d14da26038e9c213d1381",
                 "shasum": ""
             },
             "require": {
-                "nikic/php-parser": "^4.7",
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/dependency-injection": "^5.2",
+                "symplify/package-builder": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\AutowireArrayParameter\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
+                "MIT"
             ],
-            "description": "Library for calculating the complexity of PHP code units",
-            "homepage": "https://github.com/sebastianbergmann/complexity",
-            "support": {
-                "issues": "https://github.com/sebastianbergmann/complexity/issues",
-                "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+            "description": "Autowire array parameters for your Symfony applications",
+            "support": {
+                "source": "https://github.com/symplify/autowire-array-parameter/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T15:52:27+00:00"
+            "time": "2021-05-04T18:51:07+00:00"
         },
         {
-            "name": "sebastian/diff",
-            "version": "4.0.4",
+            "name": "symplify/composer-json-manipulator",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+                "url": "https://github.com/symplify/composer-json-manipulator.git",
+                "reference": "3669e146cb16d990cb33c1dcd321e462aeb63209"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
-                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+                "url": "https://api.github.com/repos/symplify/composer-json-manipulator/zipball/3669e146cb16d990cb33c1dcd321e462aeb63209",
+                "reference": "3669e146cb16d990cb33c1dcd321e462aeb63209",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/config": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/filesystem": "^4.4|^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/package-builder": "^9.3.1",
+                "symplify/smart-file-system": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3",
-                "symfony/process": "^4.2 || ^5"
+                "phpunit/phpunit": "^9.5"
             },
-            "type": "library",
+            "type": "symfony-bundle",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\ComposerJsonManipulator\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Kore Nordmann",
-                    "email": "mail@kore-nordmann.de"
-                }
-            ],
-            "description": "Diff implementation",
-            "homepage": "https://github.com/sebastianbergmann/diff",
-            "keywords": [
-                "diff",
-                "udiff",
-                "unidiff",
-                "unified diff"
+                "MIT"
             ],
+            "description": "Package to load, merge and save composer.json file(s)",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/diff/issues",
-                "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+                "source": "https://github.com/symplify/composer-json-manipulator/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:10:38+00:00"
+            "time": "2021-05-04T18:51:09+00:00"
         },
         {
-            "name": "sebastian/environment",
-            "version": "5.1.3",
+            "name": "symplify/console-color-diff",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "388b6ced16caa751030f6a69e588299fa09200ac"
+                "url": "https://github.com/symplify/console-color-diff.git",
+                "reference": "4be4c9a5951bc4e06138099582143ce965f4e63c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
-                "reference": "388b6ced16caa751030f6a69e588299fa09200ac",
+                "url": "https://api.github.com/repos/symplify/console-color-diff/zipball/4be4c9a5951bc4e06138099582143ce965f4e63c",
+                "reference": "4be4c9a5951bc4e06138099582143ce965f4e63c",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "sebastian/diff": "^3.0|^4.0",
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/package-builder": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
-            },
-            "suggest": {
-                "ext-posix": "*"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.1-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\ConsoleColorDiff\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Provides functionality to handle HHVM/PHP environments",
-            "homepage": "http://www.github.com/sebastianbergmann/environment",
-            "keywords": [
-                "Xdebug",
-                "environment",
-                "hhvm"
+                "MIT"
             ],
+            "description": "Package to print diffs in console with colors",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/environment/issues",
-                "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
+                "source": "https://github.com/symplify/console-color-diff/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T05:52:38+00:00"
+            "time": "2021-05-04T18:51:06+00:00"
         },
         {
-            "name": "sebastian/exporter",
-            "version": "4.0.3",
+            "name": "symplify/console-package-builder",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65"
+                "url": "https://github.com/symplify/console-package-builder.git",
+                "reference": "6b3c83944b8c094c5a4b80356316d2348a9056c7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65",
-                "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65",
+                "url": "https://api.github.com/repos/symplify/console-package-builder/zipball/6b3c83944b8c094c5a4b80356316d2348a9056c7",
+                "reference": "6b3c83944b8c094c5a4b80356316d2348a9056c7",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.3",
-                "sebastian/recursion-context": "^4.0"
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symplify/symplify-kernel": "^9.3.1"
             },
             "require-dev": {
-                "ext-mbstring": "*",
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/package-builder": "^9.3.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\ConsolePackageBuilder\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Adam Harvey",
-                    "email": "aharvey@php.net"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@gmail.com"
-                }
-            ],
-            "description": "Provides the functionality to export PHP variables for visualization",
-            "homepage": "http://www.github.com/sebastianbergmann/exporter",
-            "keywords": [
-                "export",
-                "exporter"
+                "MIT"
             ],
+            "description": "Package to speed up building command line applications",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/exporter/issues",
-                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3"
+                "source": "https://github.com/symplify/console-package-builder/tree/v9.3.1"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-09-28T05:24:23+00:00"
+            "time": "2021-05-04T18:51:09+00:00"
         },
         {
-            "name": "sebastian/global-state",
-            "version": "5.0.2",
+            "name": "symplify/easy-testing",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/global-state.git",
-                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
+                "url": "https://github.com/symplify/easy-testing.git",
+                "reference": "0bde7c1c45d80a6ff9587a0b960984d9dc9b9700"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
-                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
+                "url": "https://api.github.com/repos/symplify/easy-testing/zipball/0bde7c1c45d80a6ff9587a0b960984d9dc9b9700",
+                "reference": "0bde7c1c45d80a6ff9587a0b960984d9dc9b9700",
                 "shasum": ""
             },
             "require": {
+                "nette/utils": "^3.2",
                 "php": ">=7.3",
-                "sebastian/object-reflector": "^2.0",
-                "sebastian/recursion-context": "^4.0"
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/finder": "^4.4|^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/console-package-builder": "^9.3.1",
+                "symplify/package-builder": "^9.3.1",
+                "symplify/smart-file-system": "^9.3.1",
+                "symplify/symplify-kernel": "^9.3.1"
             },
             "require-dev": {
-                "ext-dom": "*",
-                "phpunit/phpunit": "^9.3"
-            },
-            "suggest": {
-                "ext-uopz": "*"
+                "phpunit/phpunit": "^9.5"
             },
-            "type": "library",
+            "bin": [
+                "bin/easy-testing"
+            ],
+            "type": "symfony-bundle",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\EasyTesting\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Snapshotting of global state",
-            "homepage": "http://www.github.com/sebastianbergmann/global-state",
-            "keywords": [
-                "global state"
+                "MIT"
             ],
+            "description": "Testing made easy",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/global-state/issues",
-                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
+                "source": "https://github.com/symplify/easy-testing/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T15:55:19+00:00"
+            "time": "2021-05-04T18:51:21+00:00"
         },
         {
-            "name": "sebastian/lines-of-code",
-            "version": "1.0.3",
+            "name": "symplify/package-builder",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/lines-of-code.git",
-                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+                "url": "https://github.com/symplify/package-builder.git",
+                "reference": "126c7b9e3c13fb44c581fced4c5355bd51466d55"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
-                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "url": "https://api.github.com/repos/symplify/package-builder/zipball/126c7b9e3c13fb44c581fced4c5355bd51466d55",
+                "reference": "126c7b9e3c13fb44c581fced4c5355bd51466d55",
                 "shasum": ""
             },
             "require": {
-                "nikic/php-parser": "^4.6",
-                "php": ">=7.3"
+                "nette/neon": "^3.2",
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/config": "^4.4|^5.2",
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/finder": "^4.4|^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/easy-testing": "^9.3.1",
+                "symplify/symplify-kernel": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\PackageBuilder\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
+                "MIT"
             ],
-            "description": "Library for counting the lines of code in PHP source code",
-            "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+            "description": "Dependency Injection, Console and Kernel toolkit for Symplify packages.",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
-                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+                "source": "https://github.com/symplify/package-builder/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-11-28T06:42:11+00:00"
+            "time": "2021-05-04T18:51:32+00:00"
         },
         {
-            "name": "sebastian/object-enumerator",
-            "version": "4.0.4",
+            "name": "symplify/rule-doc-generator-contracts",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
-                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+                "url": "https://github.com/symplify/rule-doc-generator-contracts.git",
+                "reference": "0f04552a1fad5beff718935086177ef2a4de5ca6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
-                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+                "url": "https://api.github.com/repos/symplify/rule-doc-generator-contracts/zipball/0f04552a1fad5beff718935086177ef2a4de5ca6",
+                "reference": "0f04552a1fad5beff718935086177ef2a4de5ca6",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3",
-                "sebastian/object-reflector": "^2.0",
-                "sebastian/recursion-context": "^4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\RuleDocGenerator\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
+                "MIT"
             ],
-            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
-            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "description": "Contracts for production code of RuleDocGenerator",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
-                "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+                "source": "https://github.com/symplify/rule-doc-generator-contracts/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:12:34+00:00"
+            "time": "2021-04-28T07:48:39+00:00"
         },
         {
-            "name": "sebastian/object-reflector",
-            "version": "2.0.4",
+            "name": "symplify/set-config-resolver",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/object-reflector.git",
-                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+                "url": "https://github.com/symplify/set-config-resolver.git",
+                "reference": "00a96e094b2c4e784b74797a07a2ca562d14a3b3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
-                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+                "url": "https://api.github.com/repos/symplify/set-config-resolver/zipball/00a96e094b2c4e784b74797a07a2ca562d14a3b3",
+                "reference": "00a96e094b2c4e784b74797a07a2ca562d14a3b3",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/config": "^4.4|^5.2",
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/filesystem": "^4.4|^5.2",
+                "symfony/finder": "^4.4|^5.2",
+                "symplify/smart-file-system": "^9.3.1",
+                "symplify/symplify-kernel": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\SetConfigResolver\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
+                "MIT"
             ],
-            "description": "Allows reflection of object attributes, including inherited and non-public ones",
-            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "description": "Resolve config and sets from configs and cli opptions for CLI applications",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
-                "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+                "source": "https://github.com/symplify/set-config-resolver/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:14:26+00:00"
+            "time": "2021-05-04T18:52:01+00:00"
         },
         {
-            "name": "sebastian/recursion-context",
-            "version": "4.0.4",
+            "name": "symplify/simple-php-doc-parser",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
+                "url": "https://github.com/symplify/simple-php-doc-parser.git",
+                "reference": "c3e9dfc3a33dc2d16c3beff7fefa27a3b4cf421b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
-                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
+                "url": "https://api.github.com/repos/symplify/simple-php-doc-parser/zipball/c3e9dfc3a33dc2d16c3beff7fefa27a3b4cf421b",
+                "reference": "c3e9dfc3a33dc2d16c3beff7fefa27a3b4cf421b",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.3",
+                "phpstan/phpdoc-parser": "^0.5",
+                "symfony/config": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/package-builder": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "phpunit/phpunit": "^9.5",
+                "symplify/easy-testing": "^9.3.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\SimplePhpDocParser\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Adam Harvey",
-                    "email": "aharvey@php.net"
-                }
+                "MIT"
             ],
-            "description": "Provides functionality to recursively process PHP variables",
-            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "description": "Service integration of phpstan/phpdoc-parser, with few extra goodies for practical simple use",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
-                "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+                "source": "https://github.com/symplify/simple-php-doc-parser/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:17:30+00:00"
+            "time": "2021-05-04T18:52:01+00:00"
         },
         {
-            "name": "sebastian/resource-operations",
-            "version": "3.0.3",
+            "name": "symplify/skipper",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/resource-operations.git",
-                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+                "url": "https://github.com/symplify/skipper.git",
+                "reference": "cd166ddf714cfdc120fb44bc1338865b2f59b889"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
-                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "url": "https://api.github.com/repos/symplify/skipper/zipball/cd166ddf714cfdc120fb44bc1338865b2f59b889",
+                "reference": "cd166ddf714cfdc120fb44bc1338865b2f59b889",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/config": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/filesystem": "^4.4|^5.2",
+                "symfony/finder": "^4.4|^5.2",
+                "symplify/package-builder": "^9.3.1",
+                "symplify/smart-file-system": "^9.3.1",
+                "symplify/symplify-kernel": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.0"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.0-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\Skipper\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
+                "MIT"
             ],
-            "description": "Provides a list of PHP built-in functions that operate on resources",
-            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "description": "Skip files by rule class, directory, file or fnmatch",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
-                "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+                "source": "https://github.com/symplify/skipper/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T06:45:17+00:00"
+            "time": "2021-05-04T18:52:00+00:00"
         },
         {
-            "name": "sebastian/type",
-            "version": "2.3.1",
+            "name": "symplify/smart-file-system",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/type.git",
-                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
+                "url": "https://github.com/symplify/smart-file-system.git",
+                "reference": "2721d9eb56b43726307d42a4b64f2cee5b2534b5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
-                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
+                "url": "https://api.github.com/repos/symplify/smart-file-system/zipball/2721d9eb56b43726307d42a4b64f2cee5b2534b5",
+                "reference": "2721d9eb56b43726307d42a4b64f2cee5b2534b5",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "nette/utils": "^3.2",
+                "php": ">=7.3",
+                "symfony/filesystem": "^4.4|^5.2",
+                "symfony/finder": "^4.4|^5.2"
             },
             "require-dev": {
-                "phpunit/phpunit": "^9.3"
+                "nette/finder": "^2.5",
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.3-dev"
+                    "dev-main": "9.4-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\SmartFileSystem\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
+                "MIT"
             ],
-            "description": "Collection of value objects that represent the types of the PHP type system",
-            "homepage": "https://github.com/sebastianbergmann/type",
+            "description": "Sanitized FileInfo with safe getRealPath() and other handy methods",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/type/issues",
-                "source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
+                "source": "https://github.com/symplify/smart-file-system/tree/v9.3.1"
             },
             "funding": [
                 {
-                    "url": "https://github.com/sebastianbergmann",
+                    "url": "https://www.paypal.me/rectorphp",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/tomasvotruba",
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:18:59+00:00"
+            "time": "2021-04-28T07:48:45+00:00"
         },
         {
-            "name": "sebastian/version",
-            "version": "3.0.2",
+            "name": "symplify/symfony-php-config",
+            "version": "v9.3.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/sebastianbergmann/version.git",
-                "reference": "c6c1022351a901512170118436c764e473f6de8c"
+                "url": "https://github.com/symplify/symfony-php-config.git",
+                "reference": "0a5bf9f1d7d970f2df2b4e4225c297bd774c23c8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
-                "reference": "c6c1022351a901512170118436c764e473f6de8c",
+                "url": "https://api.github.com/repos/symplify/symfony-php-config/zipball/0a5bf9f1d7d970f2df2b4e4225c297bd774c23c8",
+                "reference": "0a5bf9f1d7d970f2df2b4e4225c297bd774c23c8",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.3"
+                "php": ">=7.3",
+                "symfony/dependency-injection": "^5.2",
+                "symplify/package-builder": "^9.3",
+                "symplify/symplify-kernel": "^9.3"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.82",
+                "phpunit/phpunit": "^9.5",
+                "symfony/http-kernel": "^4.4|^5.2"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.0-dev"
+                    "dev-main": "9.3-dev"
                 }
             },
             "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+                "psr-4": {
+                    "Symplify\\SymfonyPhpConfig\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
+                "MIT"
             ],
-            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
-            "homepage": "https://github.com/sebastianbergmann/version",
+            "description": "Tools that easy work with Symfony PHP Configs",
             "support": {
-                "issues": "https://github.com/sebastianbergmann/version/issues",
-                "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+                "source": "https://github.com/symplify/symfony-php-config/tree/v9.3.0"
             },
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-09-28T06:39:44+00:00"
+            "time": "2021-04-26T17:12:31+00:00"
         },
         {
-            "name": "squizlabs/php_codesniffer",
-            "version": "3.5.8",
+            "name": "symplify/symplify-kernel",
+            "version": "v9.3.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
+                "url": "https://github.com/symplify/symplify-kernel.git",
+                "reference": "4f895a22b50579ad0767f6b18f2e0aaf8b11fcac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
-                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
+                "url": "https://api.github.com/repos/symplify/symplify-kernel/zipball/4f895a22b50579ad0767f6b18f2e0aaf8b11fcac",
+                "reference": "4f895a22b50579ad0767f6b18f2e0aaf8b11fcac",
                 "shasum": ""
             },
             "require": {
-                "ext-simplexml": "*",
-                "ext-tokenizer": "*",
-                "ext-xmlwriter": "*",
-                "php": ">=5.4.0"
+                "jean85/pretty-package-versions": "^1.6|^2.0.1",
+                "php": ">=7.3",
+                "symfony/console": "^4.4|^5.2",
+                "symfony/dependency-injection": "^5.2",
+                "symfony/http-kernel": "^4.4|^5.2",
+                "symplify/autowire-array-parameter": "^9.3.1",
+                "symplify/composer-json-manipulator": "^9.3.1",
+                "symplify/package-builder": "^9.3.1",
+                "symplify/smart-file-system": "^9.3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+                "phpunit/phpunit": "^9.5"
             },
-            "bin": [
-                "bin/phpcs",
-                "bin/phpcbf"
-            ],
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.x-dev"
+                    "dev-main": "9.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symplify\\SymplifyKernel\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Greg Sherwood",
-                    "role": "lead"
-                }
-            ],
-            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
-            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
-            "keywords": [
-                "phpcs",
-                "standards"
+                "MIT"
             ],
+            "description": "Internal Kernel for Symplify packages",
             "support": {
-                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
-                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
-                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+                "source": "https://github.com/symplify/symplify-kernel/tree/v9.3.1"
             },
-            "time": "2020-10-23T02:01:07+00:00"
+            "time": "2021-05-04T18:52:09+00:00"
         },
         {
             "name": "theseer/tokenizer",
@@ -4250,6 +8357,80 @@
             ],
             "time": "2020-07-12T23:59:07+00:00"
         },
+        {
+            "name": "tracy/tracy",
+            "version": "v2.8.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/tracy.git",
+                "reference": "cb7d3dcd9469aa2aa6722edf6bee2d5d75188079"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/tracy/zipball/cb7d3dcd9469aa2aa6722edf6bee2d5d75188079",
+                "reference": "cb7d3dcd9469aa2aa6722edf6bee2d5d75188079",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-session": "*",
+                "php": ">=7.2 <8.1"
+            },
+            "conflict": {
+                "nette/di": "<3.0"
+            },
+            "require-dev": {
+                "latte/latte": "^2.5",
+                "nette/di": "^3.0",
+                "nette/mail": "^3.0",
+                "nette/tester": "^2.2",
+                "nette/utils": "^3.0",
+                "phpstan/phpstan": "^0.12",
+                "psr/log": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.8-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src"
+                ],
+                "files": [
+                    "src/Tracy/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "😎  Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.",
+            "homepage": "https://tracy.nette.org",
+            "keywords": [
+                "Xdebug",
+                "debug",
+                "debugger",
+                "nette",
+                "profiler"
+            ],
+            "support": {
+                "issues": "https://github.com/nette/tracy/issues",
+                "source": "https://github.com/nette/tracy/tree/v2.8.4"
+            },
+            "time": "2021-04-27T21:47:10+00:00"
+        },
         {
             "name": "webmozart/assert",
             "version": "1.10.0",
diff --git a/docker-compose.yml b/docker-compose.yml
index 296854d22e..7b138da1b8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,7 +8,7 @@ services:
     build:
       context: .
       dockerfile: Dockerfile
-    container_name: "castopod_host"
+    container_name: castopod_host
     command: >
       sh -c "cron && php spark serve --host 0.0.0.0"
     ports:
@@ -22,8 +22,8 @@ services:
       - castopod
 
   redis:
-    image: "redis:alpine"
-    container_name: "castopod_redis"
+    image: redis:alpine
+    container_name: castopod_host_redis
     ports:
       - 6379:6379
     volumes:
@@ -33,7 +33,7 @@ services:
 
   mariadb:
     image: mariadb:latest
-    container_name: castopod_mariadb
+    container_name: castopod_host_mariadb
     ports:
       - 3306:3306
     volumes:
@@ -48,7 +48,7 @@ services:
 
   phpmyadmin:
     image: phpmyadmin/phpmyadmin:latest
-    container_name: castopod_phpmyadmin
+    container_name: castopod_host_phpmyadmin
     environment:
       PMA_HOST: mariadb
       PMA_PORT: 3306
@@ -61,24 +61,6 @@ services:
     networks:
       - castopod
 
-  composer:
-    image: composer:latest
-    container_name: castopod_composer
-    volumes:
-      - .:/var/www/html
-    working_dir: /var/www/html
-    networks:
-      - castopod
-
-  node:
-    image: node:lts
-    container_name: castopod_node
-    volumes:
-      - .:/usr/src/app
-    working_dir: /usr/src/app
-    networks:
-      - castopod
-
 volumes:
   redis:
   mariadb:
diff --git a/docs/setup-development.md b/docs/setup-development.md
index 5e0badd69c..a65c178f4a 100644
--- a/docs/setup-development.md
+++ b/docs/setup-development.md
@@ -3,19 +3,19 @@
 ## Table of contents <!-- omit in toc -->
 
 - [Introduction](#introduction)
-- [Prerequisites](#prerequisites)
-- [Start docker containers](#start-docker-containers)
+- [Pre-requisites](#pre-requisites)
+  - [(recommended) Develop inside the app Container with VSCode](#recommended-develop-inside-the-app-container-with-vscode)
+  - [(not-recommended) Develop outside the app container](#not-recommended-develop-outside-the-app-container)
+- [Install Castopod Host's dependencies](#install-castopod-hosts-dependencies)
 - [Initialize and populate database](#initialize-and-populate-database)
-- [Install/Update app dependencies](#installupdate-app-dependencies)
 - [Start hacking](#start-hacking)
 - [Going Further](#going-further)
   - [Useful docker / docker-compose commands](#useful-docker--docker-compose-commands)
-- [Developing inside a Container](#developing-inside-a-container)
 - [Known issues](#known-issues)
 
 ## Introduction
 
-Castopod is a web app based on the `php` framework
+Castopod Host is a web app based on the `php` framework
 [CodeIgniter 4](https://codeigniter.com).
 
 To setup a dev environment, we use [Docker](https://www.docker.com/). A
@@ -27,194 +27,244 @@ to help you kickstart your contribution.
 
 ## Pre-requisites
 
-0. Install [docker desktop](https://www.docker.com/products/docker-desktop).
+0. Install [docker](https://docs.docker.com/get-docker).
 
-1. Clone castopod project by running:
+1. Clone Castopod Host project by running:
 
-```bash
-git clone https://code.podlibre.org/podlibre/castopod.git
-```
+   ```bash
+   git clone https://code.podlibre.org/podlibre/castopod-host.git
+   ```
 
 2. Create a `.env` file with the minimum required config to connect the app to
    the database and use redis as a cache handler:
 
-```ini
-CI_ENVIRONMENT="development"
+   ```ini
+   CI_ENVIRONMENT="development"
 
-# By default, this is set to true in the app config.
-# For development, this must be set to false as it is
-# on a local environment
-app.forceGlobalSecureRequests=false
+   # By default, this is set to true in the app config.
+   # For development, this must be set to false as it is
+   # on a local environment
+   app.forceGlobalSecureRequests=false
 
-app.baseURL="http://localhost:8080/"
-app.mediaBaseURL="http://localhost:8080/"
+   app.baseURL="http://localhost:8080/"
+   app.mediaBaseURL="http://localhost:8080/"
 
-app.adminGateway="cp-admin"
-app.authGateway="cp-auth"
+   app.adminGateway="cp-admin"
+   app.authGateway="cp-auth"
 
-database.default.hostname="mariadb"
-database.default.database="castopod"
-database.default.username="podlibre"
-database.default.password="castopod"
+   database.default.hostname="mariadb"
+   database.default.database="castopod"
+   database.default.username="podlibre"
+   database.default.password="castopod"
 
-cache.handler="redis"
-cache.redis.host = "redis"
+   cache.handler="redis"
+   cache.redis.host = "redis"
 
-# You may not want to use redis as your cache handler
-# Comment/remove the two lines above and uncomment
-# the next line for file caching.
-#cache.handler="file"
-```
+   # You may not want to use redis as your cache handler
+   # Comment/remove the two lines above and uncomment
+   # the next line for file caching.
+   #cache.handler="file"
+   ```
 
-> _NB._ You can tweak your environment by setting more environment variables in
-> your custom `.env` file. See the `env` for examples or the
-> [CodeIgniter4 User Guide](https://codeigniter.com/user_guide/index.html) for
-> more info.
+   > _NB._ You can tweak your environment by setting more environment variables
+   > in your custom `.env` file. See the `env` for examples or the
+   > [CodeIgniter4 User Guide](https://codeigniter.com/user_guide/index.html)
+   > for more info.
 
-3. Add the repository you've cloned to docker desktop's `Settings` >
-   `Resources` > `File Sharing`.
-4. Install castopod's php dependencies
+3. (for docker desktop) Add the repository you've cloned to docker desktop's
+   `Settings` > `Resources` > `File Sharing`
 
-> The project's php dependencies aren't included in the repository, you have to
-> download them using the composer service defined in `docker-compose.yml`
+### (recommended) Develop inside the app Container with VSCode
 
-```bash
-docker-compose run --rm composer install --ignore-platform-reqs
-```
+If you're working in VSCode, you can take advantage of the `.devcontainer/`
+folder. It defines a development environment (dev container) with preinstalled
+requirements and VSCode extensions so you don't have to worry about them. All
+required services will be loaded automagically!
 
-5. Install castopod's js dependencies
+1. Install the VSCode extension
+   [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
+2. `Ctrl/Cmd + Shift + P` > `Open in container`
 
-> The project's js dependencies aren't included in the repository, you have to
-> download them using the node service defined in `docker-compose.yml`
+   > The VSCode window will reload inside the dev container. It may take a long
+   > time on first load as it is building all necessary services.
 
-```bash
-docker-compose run --rm node npm install
-```
+3. You're all set! 🎉
 
-6. Build assets: javascript, styles, icons and svg images
+   You're now **inside the dev container**, you may use the VSCode console
+   (`Terminal` > `New Terminal`) to run any command:
 
-> To generate public assets, you must run the following commands.
+   ```bash
+   # PHP is installed
+   php -v
 
-```bash
-docker-compose run --rm node npm run build:js
-docker-compose run --rm node npm run build:css
-docker-compose run --rm node npm run build:icons
-docker-compose run --rm node npm run build:svg
-docker-compose run --rm node npm run copy:images
-docker-compose run --rm node npm run copy:fonts
-```
+   # Composer is installed
+   composer -V
 
-## Start docker containers
+   # npm is installed
+   npm -v
 
-Go to project's root folder and run:
+   # git is installed
+   git version
+   ```
 
-```bash
-# starts all services declared in docker-compose.yml file
-# -d option starts the containers in the background
-docker-compose up -d
+For more info, see
+[VSCode Remote Containers](https://code.visualstudio.com/docs/remote/containers)
 
-# See all running processes (you should see 3 processes running)
-docker-compose ps
+### (not-recommended) Develop outside the app container
 
-# Alternatively, you can check all docker processes (you should see composer and npm with an Exited status)
-docker ps -a
+You do not wish to use the VSCode devcontainer? No problem!
 
-```
+1. Start docker containers manually:
 
-> The `docker-compose up -d` command will boot 4 containers in the background:
->
-> - `castopod_app`: a php based container with CodeIgniter4 requirements
->   installed
-> - `castopod_redis`: a [redis](https://redis.io/) database to handle queries
->   and pages caching
-> - `castopod_mariadb`: a [mariadb](https://mariadb.org/) server for persistent
->   data
-> - `castopod_phpmyadmin`: a phpmyadmin server to visualize the mariadb
->   database.
+   Go to project's root folder and run:
 
-## Initialize and populate database
+   ```bash
+   # starts all services declared in docker-compose.yml file
+   # -d option starts the containers in the background
+   docker-compose up -d
 
-1. Build the database with the migrate command:
+   # See all running processes (you should see 3 processes running)
+   docker-compose ps
 
-```bash
-# loads the database schema during first migration
-docker-compose run --rm app php spark migrate -all
-```
+   # Alternatively, you can check all docker processes
+   docker ps -a
 
-In case you need to roll back, use this command:
+   ```
 
-```
-# rolls back database schema loading (deletes all tables and their content)
-docker-compose run --rm app php spark migrate:rollback
-```
+   > The `docker-compose up -d` command will boot 4 containers in the
+   > background:
+   >
+   > - `castopod_host_app`: a php based container with CodeIgniter4 requirements
+   >   installed
+   > - `castopod_host_redis`: a [redis](https://redis.io/) database to handle
+   >   queries and pages caching
+   > - `castopod_host_mariadb`: a [mariadb](https://mariadb.org/) server for
+   >   persistent data
+   > - `castopod_host_phpmyadmin`: a phpmyadmin server to visualize the mariadb
+   >   database.
 
-2. Populate the database with the required data:
+2. Run any command by prefixing them with `docker-compose run --rm app`:
 
-```bash
-# Populates all required data
-docker-compose run --rm app php spark db:seed AppSeeder
-```
+   ```bash
+   # use PHP
+   docker-compose run --rm app php -v
 
-You may also add only data you chose:
+   # use Composer
+   docker-compose run --rm app composer -V
 
-```bash
-# Populates all categories
-docker-compose run --rm app php spark db:seed CategorySeeder
-# Populates all Languages
-docker-compose run --rm app php spark db:seed LanguageSeeder
-# Populates all podcasts platforms
-docker-compose run --rm app php spark db:seed PlatformSeeder
-# Populates all Authentication data (roles definition…)
-docker-compose run --rm app php spark db:seed AuthSeeder
-```
+   # use npm
+   docker-compose run --rm app npm -v
 
-3. (optionnal) Populate the database with test data:
+   # use git
+   docker-compose run --rm app git version
+   ```
 
-```bash
-# Populates test data (login: admin / password: AGUehL3P)
-docker-compose run --rm app php spark db:seed TestSeeder
-# Populates with fake podcast analytics
-docker-compose run --rm app php spark db:seed FakePodcastsAnalyticsSeeder
-# Populates with fake website analytics
-docker-compose run --rm app php spark db:seed FakeWebsiteAnalyticsSeeder
-```
+## Install Castopod Host's dependencies
 
-TestSeeder will add an active superadmin user with the following credentials:
+1. Install php dependencies with [Composer](https://getcomposer.org/)
 
-- username: **admin**
-- password: **AGUehL3P**
+   ```bash
+   composer install
+   ```
 
-## Install/Update app dependencies
+   > **Note:**
+   >
+   > The php dependencies aren't included in the repository. Composer will check
+   > the `composer.json` and `composer.lock` files to download the packages with
+   > the right versions. The dependencies will live under the `vendor/` folder.
+   > For more info, check out the
+   > [Composer documentation](https://getcomposer.org/doc/).
 
-Castopod uses `composer` to manage php dependencies and `npm` to manage
-javascript dependencies.
+2. Install javascript dependencies with [npm](https://www.npmjs.com/)
 
-You can install / update the project's dependencies using both `composer` and
-`node` services:
+   ```bash
+   npm install
+   ```
 
-```bash
-# install php dependencies
-docker-compose run --rm composer install --ignore-platform-reqs
+   > **Note:**
+   >
+   > The javascript dependencies aren't included in the repository. Npm will
+   > check the `package.json` and `package.lock` files to download the packages
+   > with the right versions. The dependencies will live under the `node_module`
+   > folder. For more info, check out the
+   > [NPM documentation](https://docs.npmjs.com/).
 
-# update php dependencies
-docker-compose run --rm composer update --ignore-platform-reqs
-```
+3. Generate static assets:
 
-> _NB._ composer commands look for the `composer.json` file to find castopod's
-> php dependencies, all of which live in the `vendor/` folder. For more info,
-> check out [Composer documentation](https://getcomposer.org/doc/).
+   ```bash
+   # build all assets at once
+   npm run build:dev
 
-```bash
-# install js dependencies
-docker-compose run --rm node npm install
+   # generate/copy specific assets
+   npm run build:js
+   npm run build:css
+   npm run build:icons
+   npm run build:svg
+   npm run copy:images
+   npm run copy:fonts
+   ```
 
-# update js dependencies
-docker-compose run --rm node npm update
-```
+   > **Note:**
+   >
+   > The static assets generated live under the `public/assets` folder, it
+   > includes javascript, styles, images, fonts, icons and svg files.
 
-> _NB._ npm commands look for the `package.json` file to find castopod's js
-> dependencies, all of which live in the `node_modules/` folder. For more info,
-> check out [NPM documentation](https://docs.npmjs.com/).
+## Initialize and populate database
+
+1. Build the database with the migrate command:
+
+   ```bash
+   # loads the database schema during first migration
+   php spark migrate -all
+   ```
+
+   You may need to undo the migration (rollback):
+
+   ```bash
+   # rolls back database schema (deletes all tables and their content)
+   php spark migrate:rollback
+   ```
+
+2. Populate the database with the required data:
+
+   ```bash
+   # Populates all required data
+   php spark db:seed AppSeeder
+   ```
+
+   You may choose to add data separately:
+
+   ```bash
+   # Populates all categories
+   php spark db:seed CategorySeeder
+
+   # Populates all Languages
+   php spark db:seed LanguageSeeder
+
+   # Populates all podcasts platforms
+   php spark db:seed PlatformSeeder
+
+   # Populates all Authentication data (roles definition…)
+   php spark db:seed AuthSeeder
+   ```
+
+3. (optionnal) Populate the database with test data:
+
+   ```bash
+   # Populates test data (login: admin / password: AGUehL3P)
+   php spark db:seed TestSeeder
+
+   # Populates with fake podcast analytics
+   php spark db:seed FakePodcastsAnalyticsSeeder
+
+   # Populates with fake website analytics
+   php spark db:seed FakeWebsiteAnalyticsSeeder
+   ```
+
+   TestSeeder will add an active superadmin user with the following credentials:
+
+   - username: **admin**
+   - password: **AGUehL3P**
 
 ## Start hacking
 
@@ -225,7 +275,7 @@ more insights.
 
 To see your changes, go to:
 
-- [localhost:8080](http://localhost:8080/) for the castopod app
+- [localhost:8080](http://localhost:8080/) for the Castopod Host app
 - [localhost:8888](http://localhost:8888/) for the phpmyadmin interface:
 
   - username: **podlibre**
@@ -242,7 +292,7 @@ To see your changes, go to:
 docker-compose logs --tail 50 --follow --timestamps app
 
 # interact with redis server using included redis-cli command
-docker exec -it castopod_redis redis-cli
+docker exec -it castopod_host_redis redis-cli
 
 # monitor the redis container
 docker-compose logs --tail 50 --follow --timestamps redis
@@ -267,33 +317,6 @@ Check [docker](https://docs.docker.com/engine/reference/commandline/docker/) and
 [docker-compose](https://docs.docker.com/compose/reference/) documentations for
 more insights.
 
-## Developing inside a Container
-
-If you're working in VSCode, you can take advantage of the `./.devcontainer/`
-folder. It defines a development container with preinstalled VSCode extensions
-so you don't have to worry about them. The container will be loaded with php,
-composer and git:
-
-1. Install the VSCode extension
-   [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
-2. `Ctrl/Cmd + Shift + P` > `Open in container`
-
-The VSCode window will reload inside the dev container.
-
-You can check that the required packages are running in the console
-(`Terminal` > `New Terminal`):
-
-```bash
-php -v
-
-composer -V
-
-git version
-```
-
-For more info, see
-[VSCode Remote Containers](https://code.visualstudio.com/docs/remote/containers)
-
 ## Known issues
 
 - `Allocation failed - JavaScript heap out of memory` when running `npm install`
diff --git a/package.json b/package.json
index 31391b4509..a2a21d85b9 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
   },
   "scripts": {
     "build": "npm run build:js && cross-env NODE_ENV=production npm run build:css && npm run build:icons && npm run build:svg && npm run copy:images && npm run copy:fonts",
+    "build:dev": "npm run build:js && npm run build:css && npm run build:icons && npm run build:svg && npm run copy:images && npm run copy:fonts",
     "watch:js": "rollup --config --watch",
     "build:js": "rollup --config",
     "watch:css": "postcss app/Views/_assets/styles/index.css -o public/assets/index.css -w",
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c05e50146f..8040502639 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-		bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php"
+		bootstrap="vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php"
 		backupGlobals="false"
 		colors="true"
 		convertErrorsToExceptions="true"
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000000..20ea8a67c3
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
+use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
+use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector;
+use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
+use Rector\Core\Configuration\Option;
+use Rector\Core\ValueObject\PhpVersion;
+use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
+use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
+use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
+use Rector\Set\ValueObject\SetList;
+use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+
+return static function (ContainerConfigurator $containerConfigurator): void {
+    // get parameters
+    $parameters = $containerConfigurator->parameters();
+
+    $parameters->set(Option::PATHS, [
+        __DIR__ . '/app',
+        __DIR__ . '/tests',
+        __DIR__ . '/public',
+    ]);
+
+    // Define what rule sets will be applied
+    $parameters->set(Option::SETS, [
+        SetList::PHP_73,
+        SetList::TYPE_DECLARATION,
+        SetList::TYPE_DECLARATION_STRICT,
+        SetList::CODE_QUALITY,
+        SetList::CODING_STYLE,
+        SetList::EARLY_RETURN,
+        SetList::DEAD_CODE,
+        SetList::ORDER,
+    ]);
+
+    // auto import fully qualified class names
+    $parameters->set(Option::AUTO_IMPORT_NAMES, true);
+    $parameters->set(Option::ENABLE_CACHE, true);
+    $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_73);
+
+    $parameters->set(Option::SKIP, [
+        // skip specific generated files
+        __DIR__ . '/app/Language/*/PersonsTaxonomy.php',
+
+        // skip rules from used sets
+        ChangeOrIfReturnToEarlyReturnRector::class,
+        ChangeOrIfContinueToMultiContinueRector::class,
+        EncapsedStringsToSprintfRector::class,
+        SplitStringClassConstantToClassConstFetchRector::class,
+        UnSpreadOperatorRector::class,
+
+        // skip rule in specific directory
+        StringClassNameToClassConstantRector::class => [
+            __DIR__ . '/app/Language/*',
+        ],
+    ]);
+
+    $services = $containerConfigurator->services();
+    $services->set(ConsistentPregDelimiterRector::class)->call('configure', [
+        [
+            ConsistentPregDelimiterRector::DELIMITER => '~',
+        ],
+    ]);
+};
diff --git a/prepare-release.sh b/scripts/prepare-release.sh
similarity index 100%
rename from prepare-release.sh
rename to scripts/prepare-release.sh
diff --git a/tests/README.md b/tests/README.md
index ba8aa0e254..3452c2d1ca 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -8,12 +8,12 @@ test your application. Those details can be found in the documentation.
 ## Resources
 
 - [CodeIgniter 4 User Guide on Testing](https://codeigniter4.github.io/userguide/testing/index.html)
-- [PHPUnit docs](https://phpunit.readthedocs.io/en/8.3/index.html)
+- [PHPUnit docs](https://phpunit.readthedocs.io/en/8.5/index.html)
 
 ## Requirements
 
 It is recommended to use the latest version of PHPUnit. At the time of this
-writing we are running version 8.5.2. Support for this has been built into the
+writing we are running version 8.5.13. Support for this has been built into the
 **composer.json** file that ships with CodeIgniter and can easily be installed
 via [Composer](https://getcomposer.org/) if you don't already have it installed
 globally.
@@ -86,7 +86,7 @@ provides a few that you may use directly:
 
 - `CodeIgniter\Test\CIUnitTestCase` - for basic tests with no other service
   needs
-- `CodeIgniter\Test\CIDatabaseTestCase` - for tests that need database access
+- `CodeIgniter\Test\DatabaseTestTrait` - for tests that need database access
 
 Most of the time you will want to write your own test cases to hold functions
 and services common to your test suites.
diff --git a/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php b/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php
index 360a9b00c5..2a5d623249 100644
--- a/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php
+++ b/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php
@@ -6,9 +6,12 @@ use CodeIgniter\Database\Migration;
 
 class ExampleMigration extends Migration
 {
+    /**
+     * @var string
+     */
     protected $DBGroup = 'tests';
 
-    public function up()
+    public function up(): void
     {
         $fields = [
             'name' => [
@@ -56,7 +59,7 @@ class ExampleMigration extends Migration
         $this->forge->createTable('factories');
     }
 
-    public function down()
+    public function down(): void
     {
         $this->forge->dropTable('factories');
     }
diff --git a/tests/_support/Database/Seeds/ExampleSeeder.php b/tests/_support/Database/Seeds/ExampleSeeder.php
index b5527d2144..7bf35e5e78 100644
--- a/tests/_support/Database/Seeds/ExampleSeeder.php
+++ b/tests/_support/Database/Seeds/ExampleSeeder.php
@@ -4,7 +4,7 @@ use CodeIgniter\Database\Seeder;
 
 class ExampleSeeder extends Seeder
 {
-    public function run()
+    public function run(): void
     {
         $factories = [
             [
diff --git a/tests/_support/DatabaseTestCase.php b/tests/_support/DatabaseTestCase.php
index 7145f51041..59c68af3d0 100644
--- a/tests/_support/DatabaseTestCase.php
+++ b/tests/_support/DatabaseTestCase.php
@@ -1,7 +1,14 @@
-<?php namespace Tests\Support;
+<?php
 
-class DatabaseTestCase extends \CodeIgniter\Test\CIDatabaseTestCase
+namespace Tests\Support;
+
+use CodeIgniter\Test\CIUnitTestCase;
+use CodeIgniter\Test\DatabaseTestTrait;
+
+class DatabaseTestCase extends CIUnitTestCase
 {
+    use DatabaseTestTrait;
+
     /**
      * Should the database be refreshed before each test?
      *
diff --git a/tests/_support/Libraries/ConfigReader.php b/tests/_support/Libraries/ConfigReader.php
index 6ce903bd02..9456c23aae 100644
--- a/tests/_support/Libraries/ConfigReader.php
+++ b/tests/_support/Libraries/ConfigReader.php
@@ -39,6 +39,8 @@
 
 namespace Tests\Support\Libraries;
 
+use Config\App;
+
 /**
  * Class ConfigReader
  *
@@ -46,9 +48,6 @@ namespace Tests\Support\Libraries;
  * loading external values. Used to read actual local values from
  * a config file.
  */
-class ConfigReader extends \Config\App
+class ConfigReader extends App
 {
-    public function __construct()
-    {
-    }
 }
diff --git a/tests/_support/Models/ExampleModel.php b/tests/_support/Models/ExampleModel.php
index 176c69283b..7dca520b78 100644
--- a/tests/_support/Models/ExampleModel.php
+++ b/tests/_support/Models/ExampleModel.php
@@ -4,17 +4,44 @@ use CodeIgniter\Model;
 
 class ExampleModel extends Model
 {
+    /**
+     * @var string
+     */
     protected $table = 'factories';
+    /**
+     * @var string
+     */
     protected $primaryKey = 'id';
 
+    /**
+     * @var string
+     */
     protected $returnType = 'object';
+    /**
+     * @var bool
+     */
     protected $useSoftDeletes = false;
 
+    /**
+     * @var string[]
+     */
     protected $allowedFields = ['name', 'uid', 'class', 'icon', 'summary'];
 
+    /**
+     * @var bool
+     */
     protected $useTimestamps = true;
 
+    /**
+     * @var mixed[]
+     */
     protected $validationRules = [];
+    /**
+     * @var mixed[]
+     */
     protected $validationMessages = [];
+    /**
+     * @var bool
+     */
     protected $skipValidation = false;
 }
diff --git a/tests/_support/SessionTestCase.php b/tests/_support/SessionTestCase.php
index 2f53dacad8..acfa12f7a9 100644
--- a/tests/_support/SessionTestCase.php
+++ b/tests/_support/SessionTestCase.php
@@ -1,13 +1,15 @@
 <?php namespace Tests\Support;
 
 use CodeIgniter\Session\Handlers\ArrayHandler;
+use CodeIgniter\Session\SessionInterface;
 use CodeIgniter\Test\CIUnitTestCase;
 use CodeIgniter\Test\Mock\MockSession;
+use Config\Services;
 
 class SessionTestCase extends CIUnitTestCase
 {
     /**
-     * @var SessionHandler
+     * @var SessionInterface
      */
     protected $session;
 
@@ -28,8 +30,8 @@ class SessionTestCase extends CIUnitTestCase
         $config = config('App');
         $this->session = new MockSession(
             new ArrayHandler($config, '0.0.0.0'),
-            $config
+            $config,
         );
-        \Config\Services::injectMock('session', $this->session);
+        Services::injectMock('session', $this->session);
     }
 }
diff --git a/tests/database/ExampleDatabaseTest.php b/tests/database/ExampleDatabaseTest.php
index 90762c3578..7dbe9dcc38 100644
--- a/tests/database/ExampleDatabaseTest.php
+++ b/tests/database/ExampleDatabaseTest.php
@@ -1,8 +1,11 @@
 <?php
 
+namespace Tests\Database;
+
+use Tests\Support\DatabaseTestCase;
 use Tests\Support\Models\ExampleModel;
 
-class ExampleDatabaseTest extends \Tests\Support\DatabaseTestCase
+class ExampleDatabaseTest extends DatabaseTestCase
 {
     public function setUp(): void
     {
@@ -11,7 +14,7 @@ class ExampleDatabaseTest extends \Tests\Support\DatabaseTestCase
         // Extra code to run before each test
     }
 
-    public function testModelFindAll()
+    public function testModelFindAll(): void
     {
         $model = new ExampleModel();
 
@@ -22,7 +25,7 @@ class ExampleDatabaseTest extends \Tests\Support\DatabaseTestCase
         $this->assertCount(3, $objects);
     }
 
-    public function testSoftDeleteLeavesRow()
+    public function testSoftDeleteLeavesRow(): void
     {
         $model = new ExampleModel();
         $this->setPrivateProperty($model, 'useSoftDeletes', true);
diff --git a/tests/session/ExampleSessionTest.php b/tests/session/ExampleSessionTest.php
index b0a91214de..ea98ad70cf 100644
--- a/tests/session/ExampleSessionTest.php
+++ b/tests/session/ExampleSessionTest.php
@@ -1,13 +1,16 @@
 <?php
 
-class ExampleSessionTest extends \Tests\Support\SessionTestCase
+namespace Tests\Session;
+
+use Tests\Support\SessionTestCase;
+class ExampleSessionTest extends SessionTestCase
 {
     public function setUp(): void
     {
         parent::setUp();
     }
 
-    public function testSessionSimple()
+    public function testSessionSimple(): void
     {
         $this->session->set('logged_in', 123);
 
diff --git a/tests/unit/HealthTest.php b/tests/unit/HealthTest.php
index d60b0d2555..30ae5219ab 100644
--- a/tests/unit/HealthTest.php
+++ b/tests/unit/HealthTest.php
@@ -1,5 +1,11 @@
 <?php
 
+namespace Tests\Unit;
+
+use Config\App;
+use Config\Services;
+use Tests\Support\Libraries\ConfigReader;
+
 class HealthTest extends \CodeIgniter\Test\CIUnitTestCase
 {
     public function setUp(): void
@@ -16,20 +22,38 @@ class HealthTest extends \CodeIgniter\Test\CIUnitTestCase
 
     public function testBaseUrlHasBeenSet()
     {
-        $env = $config = false;
+        $validation = Services::validation();
+        $env = false;
 
-        // First check in .env
+        // Check the baseURL in .env
         if (is_file(HOMEPATH . '.env')) {
             $env = (bool) preg_grep(
-                "/^app\.baseURL = './",
-                file(HOMEPATH . '.env')
+                '/^app\.baseURL = ./',
+                file(HOMEPATH . '.env'),
+            );
+        }
+
+        if ($env) {
+            // BaseURL in .env is a valid URL?
+            // phpunit.xml.dist sets app.baseURL in $_SERVER
+            // So if you set app.baseURL in .env, it takes precedence
+            $config = new App();
+            $this->assertTrue(
+                $validation->check($config->baseURL, 'valid_url'),
+                'baseURL "' . $config->baseURL . '" in .env is not valid URL',
             );
         }
 
-        // Then check the actual config file
-        $reader = new \Tests\Support\Libraries\ConfigReader();
-        $config = !empty($reader->baseUrl);
+        // Get the baseURL in app/Config/App.php
+        // You can't use Config\App, because phpunit.xml.dist sets app.baseURL
+        $reader = new ConfigReader();
 
-        $this->assertTrue($env || $config);
+        // BaseURL in app/Config/App.php is a valid URL?
+        $this->assertTrue(
+            $validation->check($reader->baseURL, 'valid_url'),
+            'baseURL "' .
+                $reader->baseURL .
+                '" in app/Config/App.php is not valid URL',
+        );
     }
 }
-- 
GitLab