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"><itunes:summary></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"><itunes:subtitle> + <itunes:summary></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"><content:encoded></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 →</a> + preg_replace('~\'.*\'|".*"~Us', '', $exception->getMessage()), + ) ?>" rel="noreferrer" target="_blank">search →</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'])): ?> - — <?= 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'])): ?> - — <?= 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'])): ?> + — <?= 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'])): ?> + — <?= 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')) ?> — - PHP: <?= esc(phpversion()) ?> — - CodeIgniter: <?= esc(\CodeIgniter\CodeIgniter::CI_VERSION) ?> + PHP: <?= esc(phpversion()) ?> — + 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