From 6b74a9e98ac89d1ebd6b7f8a69525e5ddfa1d220 Mon Sep 17 00:00:00 2001 From: Yassine Doghri <yassine@doghri.fr> Date: Fri, 14 May 2021 17:59:35 +0000 Subject: [PATCH] refactor: update code base to php 8 and set phpstan lvl to 6 --- .gitlab-ci.yml | 2 +- .prettierrc.json | 2 +- Dockerfile | 6 +- INSTALL.md | 6 +- app/Authorization/FlatAuthorization.php | 4 +- app/Config/ActivityPub.php | 20 +- app/Config/Analytics.php | 1 + app/Config/Database.php | 16 +- app/Config/Exceptions.php | 2 +- app/Config/Filters.php | 8 +- app/Config/Kint.php | 15 +- app/Config/Logger.php | 16 +- app/Config/Routes.php | 6 +- app/Config/Services.php | 29 +- app/Config/View.php | 4 +- app/Controllers/ActorController.php | 5 +- app/Controllers/Admin/BaseController.php | 2 +- .../Admin/ContributorController.php | 25 +- app/Controllers/Admin/EpisodeController.php | 100 +- .../Admin/EpisodePersonController.php | 29 +- app/Controllers/Admin/FediverseController.php | 6 +- app/Controllers/Admin/HomeController.php | 3 +- app/Controllers/Admin/MyAccountController.php | 7 +- app/Controllers/Admin/PageController.php | 17 +- app/Controllers/Admin/PersonController.php | 23 +- app/Controllers/Admin/PodcastController.php | 13 +- .../Admin/PodcastImportController.php | 259 +++-- .../Admin/PodcastPersonController.php | 22 +- .../Admin/PodcastPlatformController.php | 23 +- app/Controllers/Admin/UserController.php | 23 +- app/Controllers/AuthController.php | 16 +- app/Controllers/BaseController.php | 2 +- app/Controllers/EpisodeController.php | 48 +- app/Controllers/HomeController.php | 8 +- app/Controllers/InstallController.php | 52 +- app/Controllers/NoteController.php | 52 +- app/Controllers/PageController.php | 6 +- app/Controllers/PodcastController.php | 61 +- .../Seeds/FakePodcastsAnalyticsSeeder.php | 2 +- app/Entities/Actor.php | 11 +- app/Entities/Category.php | 5 +- app/Entities/Credit.php | 37 +- app/Entities/Episode.php | 166 +--- app/Entities/EpisodePerson.php | 48 - app/Entities/Image.php | 31 +- app/Entities/Note.php | 14 +- app/Entities/Page.php | 13 +- app/Entities/Person.php | 17 +- app/Entities/Podcast.php | 102 +- app/Entities/PodcastPerson.php | 46 - app/Filters/PermissionFilter.php | 8 +- app/Helpers/auth_helper.php | 6 +- app/Helpers/breadcrumb_helper.php | 5 +- app/Helpers/components_helper.php | 25 +- app/Helpers/form_helper.php | 19 +- app/Helpers/id3_helper.php | 2 +- app/Helpers/location_helper.php | 4 + app/Helpers/media_helper.php | 10 +- app/Helpers/misc_helper.php | 16 +- app/Helpers/persons_helper.php | 56 -- app/Helpers/rss_helper.php | 59 +- app/Helpers/url_helper.php | 4 +- .../Activities/AnnounceActivity.php | 3 +- app/Libraries/ActivityPub/ActivityRequest.php | 4 +- .../ActivityPub/Config/ActivityPub.php | 6 +- .../Controllers/ActorController.php | 9 +- .../Controllers/BlockController.php | 9 +- .../Controllers/NoteController.php | 10 +- .../Controllers/WebFingerController.php | 2 +- .../ActivityPub/Core/AbstractObject.php | 7 +- app/Libraries/ActivityPub/Core/ObjectType.php | 6 +- app/Libraries/ActivityPub/Entities/Note.php | 2 +- .../ActivityPub/Filters/ActivityPubFilter.php | 6 +- .../Helpers/activitypub_helper.php | 25 +- app/Libraries/ActivityPub/HttpSignature.php | 8 +- .../ActivityPub/Models/ActivityModel.php | 12 +- .../ActivityPub/Models/ActorModel.php | 15 +- .../ActivityPub/Models/BlockedDomainModel.php | 19 +- .../ActivityPub/Models/FavouriteModel.php | 6 +- .../ActivityPub/Models/FollowModel.php | 4 +- .../ActivityPub/Models/NoteModel.php | 56 +- .../ActivityPub/Models/PreviewCardModel.php | 9 +- .../ActivityPub/Objects/ActorObject.php | 6 +- .../ActivityPub/Objects/NoteObject.php | 5 +- .../Objects/OrderedCollectionObject.php | 11 +- app/Libraries/ActivityPub/WebFinger.php | 22 +- app/Libraries/Analytics/AnalyticsTrait.php | 1 + app/Libraries/Analytics/Config/Analytics.php | 2 +- .../Controllers/AnalyticsController.php | 6 +- .../EpisodeAnalyticsController.php | 2 +- .../Controllers/EpisodeController.php | 200 ++++ .../UnknownUserAgentsController.php | 4 +- .../Entities/AnalyticsPodcastsByCountry.php | 2 +- .../Entities/AnalyticsPodcastsByRegion.php | 2 +- .../Entities/AnalyticsPodcastsByService.php | 2 +- ...ents.php => AnalyticsUnknownUserAgent.php} | 9 +- .../Analytics/Helpers/analytics_helper.php | 21 +- .../AnalyticsUnknownUseragentsModel.php | 18 +- .../Models/UnknownUserAgentsModel.php | 26 - app/Libraries/Breadcrumb.php | 11 +- app/Libraries/Negotiate.php | 3 + app/Libraries/Router.php | 14 +- app/Models/ActorModel.php | 3 + app/Models/CategoryModel.php | 18 +- app/Models/EpisodeModel.php | 14 +- app/Models/EpisodePersonModel.php | 159 ---- app/Models/LanguageModel.php | 8 +- app/Models/NoteModel.php | 13 +- app/Models/PageModel.php | 2 + app/Models/PersonModel.php | 211 +++- app/Models/PlatformModel.php | 52 +- app/Models/PodcastModel.php | 118 ++- app/Models/PodcastPersonModel.php | 159 ---- app/Models/SoundbiteModel.php | 6 +- app/Models/UserModel.php | 11 +- app/Validation/FileRules.php | 2 +- app/Views/errors/cli/error_exception.php | 4 +- composer.json | 12 +- composer.lock | 901 ++++++++---------- phpstan.neon | 10 +- public/index.php | 6 +- rector.php | 21 +- spark | 2 +- tests/unit/HealthTest.php | 5 +- 124 files changed, 1810 insertions(+), 2157 deletions(-) delete mode 100644 app/Entities/EpisodePerson.php delete mode 100644 app/Entities/PodcastPerson.php delete mode 100644 app/Helpers/persons_helper.php create mode 100644 app/Libraries/Analytics/Controllers/EpisodeController.php rename app/Libraries/Analytics/Entities/{AnalyticsUnknownUseragents.php => AnalyticsUnknownUserAgent.php} (74%) delete mode 100644 app/Libraries/Analytics/Models/UnknownUserAgentsModel.php delete mode 100644 app/Models/EpisodePersonModel.php delete mode 100644 app/Models/PodcastPersonModel.php diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0ce56bb888..62cc6540dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: php:7.3-fpm +image: php:8.0-fpm stages: - quality diff --git a/.prettierrc.json b/.prettierrc.json index a766ac8d82..4f59557607 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -4,7 +4,7 @@ { "files": "*.php", "options": { - "phpVersion": "7.3", + "phpVersion": "7.4", "singleQuote": true } }, diff --git a/Dockerfile b/Dockerfile index fce76183d6..44e3f9a5c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ # should be used only for development purposes #################################################### -FROM php:7.3-fpm +FROM php:8.0-fpm LABEL maintainer="Yassine Doghri<yassine@podlibre.org>" @@ -37,8 +37,8 @@ RUN apt-get update && apt-get install -y \ zlib1g-dev \ && docker-php-ext-install intl -RUN docker-php-ext-configure gd --with-jpeg-dir=/usr/include/ \ - && docker-php-ext-install gd +RUN docker-php-ext-configure gd --with-jpeg \ + && docker-php-ext-install gd RUN pecl install -o -f redis \ && rm -rf /tmp/pear \ diff --git a/INSTALL.md b/INSTALL.md index 28bb59b148..295c4e740f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,7 +10,7 @@ or shared hosting, you can install it on most PHP-MySQL compatible web servers. - [1. Install Wizard](#1-install-wizard) - [1-alt Manual configuration](#1-alt-manual-configuration) - [Web Server Requirements](#web-server-requirements) - - [PHP v7.3 or higher](#php-v73-or-higher) + - [PHP v8.0 or higher](#php-v73-or-higher) - [MySQL compatible database](#mysql-compatible-database) - [Privileges](#privileges) - [(Optional) Other recommendations](#optional-other-recommendations) @@ -59,9 +59,9 @@ through the install wizard, you can create and update the `.env` file yourself: ## Web Server Requirements -### PHP v7.3 or higher +### PHP v8.0 or higher -PHP version 7.3 or higher is required, with the following extensions installed: +PHP version 8.0 or higher is required, with the following extensions installed: - [intl](https://php.net/manual/en/intl.requirements.php) - [libcurl](https://php.net/manual/en/curl.requirements.php) diff --git a/app/Authorization/FlatAuthorization.php b/app/Authorization/FlatAuthorization.php index b5af66d5b0..f7015aa543 100644 --- a/app/Authorization/FlatAuthorization.php +++ b/app/Authorization/FlatAuthorization.php @@ -17,10 +17,8 @@ class FlatAuthorization extends MythAuthFlatAuthorization /** * Checks a group to see if they have the specified permission. - * - * @param int|string $permission */ - public function groupHasPermission($permission, int $groupId): bool + public function groupHasPermission(int|string $permission, int $groupId): bool { // Get the Permission ID $permissionId = $this->getPermissionID($permission); diff --git a/app/Config/ActivityPub.php b/app/Config/ActivityPub.php index 062603dbc1..976064542c 100644 --- a/app/Config/ActivityPub.php +++ b/app/Config/ActivityPub.php @@ -1,5 +1,7 @@ <?php namespace Config; +use App\Libraries\PodcastActor; +use App\Libraries\NoteObject; use ActivityPub\Config\ActivityPub as ActivityPubBase; class ActivityPub extends ActivityPubBase @@ -8,18 +10,32 @@ class ActivityPub extends ActivityPubBase * -------------------------------------------------------------------- * ActivityPub Objects * -------------------------------------------------------------------- + * @var string */ - public $actorObject = 'App\Libraries\PodcastActor'; - public $noteObject = 'App\Libraries\NoteObject'; + public $actorObject = PodcastActor::class; + /** + * @var string + */ + public $noteObject = NoteObject::class; /** * -------------------------------------------------------------------- * Default avatar and cover images * -------------------------------------------------------------------- + * @var string */ public $defaultAvatarImagePath = 'assets/images/castopod-avatar-default.jpg'; + /** + * @var string + */ public $defaultAvatarImageMimetype = 'image/jpeg'; + /** + * @var string + */ public $defaultCoverImagePath = 'assets/images/castopod-cover-default.jpg'; + /** + * @var string + */ public $defaultCoverImageMimetype = 'image/jpeg'; } diff --git a/app/Config/Analytics.php b/app/Config/Analytics.php index df73c9a955..66fe694573 100644 --- a/app/Config/Analytics.php +++ b/app/Config/Analytics.php @@ -10,6 +10,7 @@ class Analytics extends AnalyticsBase * -------------------------------------------------------------------- * Route filters options * -------------------------------------------------------------------- + * @var array<string, string> */ public $routeFilters = [ 'analytics-full-data' => 'permission:podcasts-view,podcast-view', diff --git a/app/Config/Database.php b/app/Config/Database.php index 4ee26d9513..a854688a5a 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -12,25 +12,21 @@ class Database extends Config /** * The directory that holds the Migrations * and Seeds directories. - * - * @var string */ - public $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR; + public string $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR; /** * Lets you choose which connection group to * use if no other is specified. - * - * @var string */ - public $defaultGroup = 'default'; + public string $defaultGroup = 'default'; /** * The default database connection. * - * @var array + * @var array<string, string|bool|int|array> */ - public $default = [ + public array $default = [ 'DSN' => '', 'hostname' => 'localhost', 'username' => '', @@ -54,9 +50,9 @@ class Database extends Config * This database connection is used when * running PHPUnit database tests. * - * @var array + * @var array<string, string|bool|int|array> */ - public $tests = [ + public array $tests = [ 'DSN' => '', 'hostname' => '127.0.0.1', 'username' => '', diff --git a/app/Config/Exceptions.php b/app/Config/Exceptions.php index b3bef25c53..47b8575762 100644 --- a/app/Config/Exceptions.php +++ b/app/Config/Exceptions.php @@ -29,7 +29,7 @@ class Exceptions extends BaseConfig * Any status codes here will NOT be logged if logging is turned on. * By default, only 404 (Page Not Found) exceptions are ignored. * - * @var array + * @var int[] */ public $ignoreCodes = [404]; diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 6a70932800..573fc1ee23 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -17,7 +17,7 @@ class Filters extends BaseConfig * Configures aliases for Filter classes to * make reading things nicer and simpler. * - * @var array + * @var array<string, string> */ public $aliases = [ 'csrf' => CSRF::class, @@ -33,7 +33,7 @@ class Filters extends BaseConfig * List of filter aliases that are always * applied before and after every request. * - * @var array + * @var array<string, string[]> */ public $globals = [ 'before' => [ @@ -53,7 +53,7 @@ class Filters extends BaseConfig * Example: * 'post' => ['csrf', 'throttle'] * - * @var array + * @var array<string, string[]> */ public $methods = []; @@ -64,7 +64,7 @@ class Filters extends BaseConfig * Example: * 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']] * - * @var array + * @var array<string, array<string, string[]>> */ public $filters = []; diff --git a/app/Config/Kint.php b/app/Config/Kint.php index 8d5ed9e0ce..ce2ff28a8f 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -23,7 +23,10 @@ class Kint extends BaseConfig |-------------------------------------------------------------------------- */ - public $plugins; + /** + * @var string[] + */ + public $plugins = []; /** * @var int @@ -60,9 +63,15 @@ class Kint extends BaseConfig */ public $richSort = Renderer::SORT_FULL; - public $richObjectPlugins; + /** + * @var string[] + */ + public $richObjectPlugins = []; - public $richTabPlugins; + /** + * @var string[] + */ + public $richTabPlugins = []; /* |-------------------------------------------------------------------------- diff --git a/app/Config/Logger.php b/app/Config/Logger.php index 5607dcfb32..cfee275721 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -36,9 +36,9 @@ class Logger extends BaseConfig * For a live site you'll usually enable Critical or higher (3) to be logged otherwise * your log files will fill up very fast. * - * @var integer|array + * @var integer|int[] */ - public $threshold = 4; + public int|array $threshold = 4; /** * -------------------------------------------------------------------------- @@ -50,7 +50,7 @@ class Logger extends BaseConfig * * @var string */ - public $dateFormat = 'Y-m-d H:i:s'; + public string $dateFormat = 'Y-m-d H:i:s'; /** * -------------------------------------------------------------------------- @@ -75,9 +75,9 @@ class Logger extends BaseConfig * Handlers are executed in the order defined in this array, starting with * the handler on top and continuing down. * - * @var array + * @var array<string, string|int|array<string, string>> */ - public $handlers = [ + public array $handlers = [ /* * -------------------------------------------------------------------- * File Handler @@ -125,9 +125,9 @@ class Logger extends BaseConfig ], /** - * The ChromeLoggerHandler requires the use of the Chrome web browser - * and the ChromeLogger extension. Uncomment this block to use it. - */ + * The ChromeLoggerHandler requires the use of the Chrome web browser + * and the ChromeLogger extension. Uncomment this block to use it. + */ // 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [ // /* // * The log levels that this handler will handle. diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 4539867c1e..a279b940ff 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -164,13 +164,13 @@ $routes->group( ]); $routes->group('persons', function ($routes): void { - $routes->get('/', 'PodcastPersonController/$1', [ + $routes->get('/', 'PodcastPodcastController/$1', [ 'as' => 'podcast-person-manage', 'filter' => 'permission:podcast-edit', ]); $routes->post( '/', - 'PodcastPersonController::attemptAdd/$1', + 'PodcastPodcastController::attemptAdd/$1', [ 'filter' => 'permission:podcast-edit', ], @@ -178,7 +178,7 @@ $routes->group( $routes->get( '(:num)/remove', - 'PodcastPersonController::remove/$1/$2', + 'PodcastPodcastController::remove/$1/$2', [ 'as' => 'podcast-person-remove', 'filter' => 'permission:podcast-edit', diff --git a/app/Config/Services.php b/app/Config/Services.php index 7ea9a3dd60..5228dd391a 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -34,18 +34,12 @@ class Services extends BaseService /** * The Router class uses a RouteCollection's array of routes, and determines * the correct Controller and Method to execute. - * - * @param RouteCollectionInterface|null $routes - * @param Request|null $request - * @param boolean $getShared - * - * @return Router */ public static function router( - RouteCollectionInterface $routes = null, - Request $request = null, + ?RouteCollectionInterface $routes = null, + ?Request $request = null, bool $getShared = true - ) { + ): Router { if ($getShared) { return static::getSharedInstance('router', $routes, $request); } @@ -60,16 +54,11 @@ class Services extends BaseService * The Negotiate class provides the content negotiation features for * working the request to determine correct language, encoding, charset, * and more. - * - * @param RequestInterface|null $request - * @param boolean $getShared - * - * @return Negotiate */ public static function negotiator( - RequestInterface $request = null, + ?RequestInterface $request = null, bool $getShared = true - ) { + ): Negotiate { if ($getShared) { return static::getSharedInstance('negotiator', $request); } @@ -79,6 +68,9 @@ class Services extends BaseService return new Negotiate($request); } + /** + * @return mixed + */ public static function authentication( string $lib = 'local', Model $userModel = null, @@ -112,6 +104,9 @@ class Services extends BaseService return $instance->setUserModel($userModel)->setLoginModel($loginModel); } + /** + * @return mixed + */ public static function authorization( Model $groupModel = null, Model $permissionModel = null, @@ -144,7 +139,7 @@ class Services extends BaseService return $instance->setUserModel($userModel); } - public static function breadcrumb(bool $getShared = true) + public static function breadcrumb(bool $getShared = true): Breadcrumb { if ($getShared) { return self::getSharedInstance('breadcrumb'); diff --git a/app/Config/View.php b/app/Config/View.php index 7697cfc927..5014fdf157 100644 --- a/app/Config/View.php +++ b/app/Config/View.php @@ -29,7 +29,7 @@ class View extends BaseView * { title|esc(js) } * { created_on|date(Y-m-d)|esc(attr) } * - * @var array + * @var string[] */ public $filters = []; @@ -38,7 +38,7 @@ class View extends BaseView * by the core Parser by creating aliases that will be replaced with * any callable. Can be single or tag pair. * - * @var array + * @var string[] */ public $plugins = []; } diff --git a/app/Controllers/ActorController.php b/app/Controllers/ActorController.php index cbc27c1c95..15cd0fae48 100644 --- a/app/Controllers/ActorController.php +++ b/app/Controllers/ActorController.php @@ -15,9 +15,12 @@ class ActorController extends ActivityPubActorController { use AnalyticsTrait; + /** + * @var string[] + */ protected $helpers = ['auth', 'svg', 'components', 'misc']; - public function follow() + public function follow(): string { // Prevent analytics hit when authenticated if (!can_user_interact()) { diff --git a/app/Controllers/Admin/BaseController.php b/app/Controllers/Admin/BaseController.php index 43deabd6cc..3150118664 100644 --- a/app/Controllers/Admin/BaseController.php +++ b/app/Controllers/Admin/BaseController.php @@ -25,7 +25,7 @@ class BaseController extends Controller * class instantiation. These helpers will be available * to all other controllers that extend BaseController. * - * @var array + * @var string[] */ protected $helpers = ['auth', 'breadcrumb', 'svg', 'components', 'misc']; diff --git a/app/Controllers/Admin/ContributorController.php b/app/Controllers/Admin/ContributorController.php index 2726abb1f5..bcdc22145e 100644 --- a/app/Controllers/Admin/ContributorController.php +++ b/app/Controllers/Admin/ContributorController.php @@ -15,6 +15,7 @@ use Exception; use App\Authorization\GroupModel; use App\Models\PodcastModel; use App\Models\UserModel; +use CodeIgniter\HTTP\RedirectResponse; class ContributorController extends BaseController { @@ -28,9 +29,9 @@ class ContributorController extends BaseController */ protected $user; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { - $this->podcast = (new PodcastModel())->getPodcastById($params[0]); + $this->podcast = (new PodcastModel())->getPodcastById((int) $params[0]); if (count($params) <= 1) { return $this->$method(); @@ -38,8 +39,8 @@ class ContributorController extends BaseController if ( $this->user = (new UserModel())->getPodcastContributor( - $params[1], - $params[0], + (int) $params[1], + (int) $params[0], ) ) { return $this->$method(); @@ -48,7 +49,7 @@ class ContributorController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function list() + public function list(): string { $data = [ 'podcast' => $this->podcast, @@ -58,7 +59,7 @@ class ContributorController extends BaseController return view('admin/contributor/list', $data); } - public function view() + public function view(): string { $data = [ 'contributor' => (new UserModel())->getPodcastContributor( @@ -74,7 +75,7 @@ class ContributorController extends BaseController return view('admin/contributor/view', $data); } - public function add() + public function add(): string { helper('form'); @@ -108,7 +109,7 @@ class ContributorController extends BaseController return view('admin/contributor/add', $data); } - public function attemptAdd() + public function attemptAdd(): RedirectResponse { try { (new PodcastModel())->addPodcastContributor( @@ -116,7 +117,7 @@ class ContributorController extends BaseController $this->podcast->id, $this->request->getPost('role'), ); - } catch (Exception $exception) { + } catch (Exception) { return redirect() ->back() ->withInput() @@ -128,7 +129,7 @@ class ContributorController extends BaseController return redirect()->route('contributor-list', [$this->podcast->id]); } - public function edit() + public function edit(): string { helper('form'); @@ -159,7 +160,7 @@ class ContributorController extends BaseController return view('admin/contributor/edit', $data); } - public function attemptEdit() + public function attemptEdit(): RedirectResponse { (new PodcastModel())->updatePodcastContributor( $this->user->id, @@ -170,7 +171,7 @@ class ContributorController extends BaseController return redirect()->route('contributor-list', [$this->podcast->id]); } - public function remove() + public function remove(): RedirectResponse { if ($this->podcast->created_by === $this->user->id) { return redirect() diff --git a/app/Controllers/Admin/EpisodeController.php b/app/Controllers/Admin/EpisodeController.php index ed81106e9a..423d1b3ba9 100644 --- a/app/Controllers/Admin/EpisodeController.php +++ b/app/Controllers/Admin/EpisodeController.php @@ -8,6 +8,9 @@ namespace App\Controllers\Admin; +use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\HTTP\RedirectResponse; +use Config\Database; use App\Entities\Episode; use App\Entities\Note; use App\Entities\Podcast; @@ -29,12 +32,14 @@ class EpisodeController extends BaseController */ protected $episode; - public function _remap(string $method, ...$params) + public function _remap(string $method, string ...$params): mixed { if ( - !($this->podcast = (new PodcastModel())->getPodcastById($params[0])) + ($this->podcast = (new PodcastModel())->getPodcastById( + (int) $params[0], + )) === null ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } if (count($params) > 1) { @@ -46,7 +51,7 @@ class EpisodeController extends BaseController ]) ->first()) ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } unset($params[1]); @@ -56,7 +61,7 @@ class EpisodeController extends BaseController return $this->$method(...$params); } - public function list() + public function list(): string { $episodes = (new EpisodeModel()) ->where('podcast_id', $this->podcast->id) @@ -74,7 +79,7 @@ class EpisodeController extends BaseController return view('admin/episode/list', $data); } - public function view() + public function view(): string { $data = [ 'podcast' => $this->podcast, @@ -88,7 +93,7 @@ class EpisodeController extends BaseController return view('admin/episode/view', $data); } - public function create() + public function create(): string { helper(['form']); @@ -102,7 +107,7 @@ class EpisodeController extends BaseController return view('admin/episode/create', $data); } - public function attemptCreate() + public function attemptCreate(): RedirectResponse { $rules = [ 'audio_file' => 'uploaded[audio_file]|ext_in[audio_file,mp3,m4a]', @@ -204,7 +209,7 @@ class EpisodeController extends BaseController ]); } - public function edit() + public function edit(): string { helper(['form']); @@ -220,7 +225,7 @@ class EpisodeController extends BaseController return view('admin/episode/edit', $data); } - public function attemptEdit() + public function attemptEdit(): RedirectResponse { $rules = [ 'audio_file' => @@ -282,17 +287,14 @@ class EpisodeController extends BaseController } } elseif ($transcriptChoice === 'remote-url') { if ( - $transcriptFileRemoteUrl = $this->request->getPost( + ($transcriptFileRemoteUrl = $this->request->getPost( 'transcript_file_remote_url', - ) + )) && + (($transcriptFile = $this->episode->transcript_file) && + $transcriptFile !== null) ) { - if ( - ($transcriptFile = $this->episode->transcript_file) && - $transcriptFile !== null - ) { - unlink($transcriptFile); - $this->episode->transcript_file_path = null; - } + unlink($transcriptFile); + $this->episode->transcript_file_path = null; } $this->episode->transcript_file_remote_url = $transcriptFileRemoteUrl; } @@ -306,17 +308,14 @@ class EpisodeController extends BaseController } } elseif ($chaptersChoice === 'remote-url') { if ( - $chaptersFileRemoteUrl = $this->request->getPost( + ($chaptersFileRemoteUrl = $this->request->getPost( 'chapters_file_remote_url', - ) + )) && + (($chaptersFile = $this->episode->chapters_file) && + $chaptersFile !== null) ) { - if ( - ($chaptersFile = $this->episode->chapters_file) && - $chaptersFile !== null - ) { - unlink($chaptersFile); - $this->episode->chapters_file_path = null; - } + unlink($chaptersFile); + $this->episode->chapters_file_path = null; } $this->episode->chapters_file_remote_url = $chaptersFileRemoteUrl; } @@ -351,7 +350,7 @@ class EpisodeController extends BaseController ]); } - public function transcriptDelete() + public function transcriptDelete(): RedirectResponse { unlink($this->episode->transcript_file); $this->episode->transcript_file_path = null; @@ -368,7 +367,7 @@ class EpisodeController extends BaseController return redirect()->back(); } - public function chaptersDelete() + public function chaptersDelete(): RedirectResponse { unlink($this->episode->chapters_file); $this->episode->chapters_file_path = null; @@ -385,7 +384,7 @@ class EpisodeController extends BaseController return redirect()->back(); } - public function publish() + public function publish(): string { if ($this->episode->publication_status === 'not_published') { helper(['form']); @@ -400,12 +399,12 @@ class EpisodeController extends BaseController 1 => $this->episode->title, ]); return view('admin/episode/publish', $data); - } else { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } + + throw PageNotFoundException::forPageNotFound(); } - public function attemptPublish() + public function attemptPublish(): RedirectResponse { $rules = [ 'publication_method' => 'required', @@ -420,7 +419,7 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); $newNote = new Note([ @@ -482,7 +481,7 @@ class EpisodeController extends BaseController ]); } - public function publishEdit() + public function publishEdit(): string { if ($this->episode->publication_status === 'scheduled') { helper(['form']); @@ -503,12 +502,11 @@ class EpisodeController extends BaseController 1 => $this->episode->title, ]); return view('admin/episode/publish_edit', $data); - } else { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } + throw PageNotFoundException::forPageNotFound(); } - public function attemptPublishEdit() + public function attemptPublishEdit(): RedirectResponse { $rules = [ 'note_id' => 'required', @@ -524,7 +522,7 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); $note = (new NoteModel())->getNoteById( @@ -584,7 +582,7 @@ class EpisodeController extends BaseController ]); } - public function unpublish() + public function unpublish(): string { if ($this->episode->publication_status === 'published') { helper(['form']); @@ -599,12 +597,12 @@ class EpisodeController extends BaseController 1 => $this->episode->title, ]); return view('admin/episode/unpublish', $data); - } else { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } + + throw PageNotFoundException::forPageNotFound(); } - public function attemptUnpublish() + public function attemptUnpublish(): RedirectResponse { $rules = [ 'understand' => 'required', @@ -617,7 +615,7 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); @@ -650,14 +648,14 @@ class EpisodeController extends BaseController ]); } - public function delete() + public function delete(): RedirectResponse { (new EpisodeModel())->delete($this->episode->id); return redirect()->route('episode-list', [$this->podcast->id]); } - public function soundbitesEdit() + public function soundbitesEdit(): string { helper(['form']); @@ -673,7 +671,7 @@ class EpisodeController extends BaseController return view('admin/episode/soundbites', $data); } - public function soundbitesAttemptEdit() + public function soundbitesAttemptEdit(): RedirectResponse { $soundbites_array = $this->request->getPost('soundbites_array'); $rules = [ @@ -682,7 +680,7 @@ class EpisodeController extends BaseController 'soundbites_array.0.duration' => 'permit_empty|required_with[soundbites_array.0.start_time]|decimal|greater_than_equal_to[0]', ]; - foreach ($soundbites_array as $soundbite_id => $soundbite) { + foreach (array_keys($soundbites_array) as $soundbite_id) { $rules += [ "soundbites_array.{$soundbite_id}.start_time" => 'required|decimal|greater_than_equal_to[0]', "soundbites_array.{$soundbite_id}.duration" => 'required|decimal|greater_than_equal_to[0]', @@ -728,7 +726,7 @@ class EpisodeController extends BaseController ]); } - public function soundbiteDelete($soundbiteId) + public function soundbiteDelete(int $soundbiteId): RedirectResponse { (new SoundbiteModel())->deleteSoundbite( $this->podcast->id, @@ -742,7 +740,7 @@ class EpisodeController extends BaseController ]); } - public function embeddablePlayer() + public function embeddablePlayer(): string { helper(['form']); diff --git a/app/Controllers/Admin/EpisodePersonController.php b/app/Controllers/Admin/EpisodePersonController.php index 7c882513f8..c38c571c42 100644 --- a/app/Controllers/Admin/EpisodePersonController.php +++ b/app/Controllers/Admin/EpisodePersonController.php @@ -8,27 +8,20 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use App\Entities\Podcast; use App\Entities\Episode; use CodeIgniter\Exceptions\PageNotFoundException; -use App\Models\EpisodePersonModel; use App\Models\PodcastModel; use App\Models\EpisodeModel; use App\Models\PersonModel; class EpisodePersonController extends BaseController { - /** - * @var Podcast - */ - protected $podcast; + protected Podcast $podcast; + protected Episode $episode; - /** - * @var Episode - */ - protected $episode; - - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) <= 2) { throw PageNotFoundException::forPageNotFound(); @@ -36,7 +29,7 @@ class EpisodePersonController extends BaseController if ( ($this->podcast = (new PodcastModel())->getPodcastById( - $params[0], + (int) $params[0], )) && ($this->episode = (new EpisodeModel()) ->where([ @@ -54,14 +47,14 @@ class EpisodePersonController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { helper('form'); $data = [ 'episode' => $this->episode, 'podcast' => $this->podcast, - 'episodePersons' => (new EpisodePersonModel())->getEpisodePersons( + 'episodePersons' => (new PersonModel())->getEpisodePersons( $this->podcast->id, $this->episode->id, ), @@ -75,7 +68,7 @@ class EpisodePersonController extends BaseController return view('admin/episode/person', $data); } - public function attemptAdd() + public function attemptAdd(): RedirectResponse { $rules = [ 'person' => 'required', @@ -88,7 +81,7 @@ class EpisodePersonController extends BaseController ->with('errors', $this->validator->getErrors()); } - (new EpisodePersonModel())->addEpisodePersons( + (new PersonModel())->addEpisodePersons( $this->podcast->id, $this->episode->id, $this->request->getPost('person'), @@ -98,9 +91,9 @@ class EpisodePersonController extends BaseController return redirect()->back(); } - public function remove($episodePersonId) + public function remove(int $episodePersonId): RedirectResponse { - (new EpisodePersonModel())->removeEpisodePersons( + (new PersonModel())->removeEpisodePersons( $this->podcast->id, $this->episode->id, $episodePersonId, diff --git a/app/Controllers/Admin/FediverseController.php b/app/Controllers/Admin/FediverseController.php index fd54d103e2..c709dd835d 100644 --- a/app/Controllers/Admin/FediverseController.php +++ b/app/Controllers/Admin/FediverseController.php @@ -10,12 +10,12 @@ namespace App\Controllers\Admin; class FediverseController extends BaseController { - public function dashboard() + public function dashboard(): string { return view('admin/fediverse/dashboard'); } - public function blockedActors() + public function blockedActors(): string { helper(['form']); @@ -26,7 +26,7 @@ class FediverseController extends BaseController ]); } - public function blockedDomains() + public function blockedDomains(): string { helper(['form']); diff --git a/app/Controllers/Admin/HomeController.php b/app/Controllers/Admin/HomeController.php index d2b973f9d7..1b515a6bc1 100644 --- a/app/Controllers/Admin/HomeController.php +++ b/app/Controllers/Admin/HomeController.php @@ -8,9 +8,10 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; class HomeController extends BaseController { - public function index() + public function index(): RedirectResponse { session()->keepFlashdata('message'); return redirect()->route('podcast-list'); diff --git a/app/Controllers/Admin/MyAccountController.php b/app/Controllers/Admin/MyAccountController.php index e482e21404..074e0785db 100644 --- a/app/Controllers/Admin/MyAccountController.php +++ b/app/Controllers/Admin/MyAccountController.php @@ -8,24 +8,25 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use Config\Services; use App\Models\UserModel; class MyAccountController extends BaseController { - public function index() + public function index(): string { return view('admin/my_account/view'); } - public function changePassword() + public function changePassword(): string { helper('form'); return view('admin/my_account/change_password'); } - public function attemptChange() + public function attemptChange(): RedirectResponse { $auth = Services::authentication(); $userModel = new UserModel(); diff --git a/app/Controllers/Admin/PageController.php b/app/Controllers/Admin/PageController.php index 520ce08d43..2308f3db8c 100644 --- a/app/Controllers/Admin/PageController.php +++ b/app/Controllers/Admin/PageController.php @@ -8,6 +8,7 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use App\Entities\Page; use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PageModel; @@ -19,7 +20,7 @@ class PageController extends BaseController */ protected $page; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); @@ -32,7 +33,7 @@ class PageController extends BaseController throw PageNotFoundException::forPageNotFound(); } - function list() + function list(): string { $data = [ 'pages' => (new PageModel())->findAll(), @@ -41,19 +42,19 @@ class PageController extends BaseController return view('admin/page/list', $data); } - function view() + function view(): string { return view('admin/page/view', ['page' => $this->page]); } - function create() + function create(): string { helper('form'); return view('admin/page/create'); } - function attemptCreate() + function attemptCreate(): RedirectResponse { $page = new Page([ 'title' => $this->request->getPost('title'), @@ -80,7 +81,7 @@ class PageController extends BaseController ); } - function edit() + function edit(): string { helper('form'); @@ -88,7 +89,7 @@ class PageController extends BaseController return view('admin/page/edit', ['page' => $this->page]); } - function attemptEdit() + function attemptEdit(): RedirectResponse { $this->page->title = $this->request->getPost('title'); $this->page->slug = $this->request->getPost('slug'); @@ -106,7 +107,7 @@ class PageController extends BaseController return redirect()->route('page-list'); } - public function delete() + public function delete(): RedirectResponse { (new PageModel())->delete($this->page->id); diff --git a/app/Controllers/Admin/PersonController.php b/app/Controllers/Admin/PersonController.php index 513d521d78..7d32264b30 100644 --- a/app/Controllers/Admin/PersonController.php +++ b/app/Controllers/Admin/PersonController.php @@ -8,6 +8,7 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use App\Entities\Person; use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PersonModel; @@ -19,27 +20,31 @@ class PersonController extends BaseController */ protected $person; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); } - if ($this->person = (new PersonModel())->getPersonById($params[0])) { + if ( + ($this->person = (new PersonModel())->getPersonById( + (int) $params[0], + )) !== null + ) { return $this->$method(); } throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { $data = ['persons' => (new PersonModel())->findAll()]; return view('admin/person/list', $data); } - public function view() + public function view(): string { $data = ['person' => $this->person]; @@ -47,14 +52,14 @@ class PersonController extends BaseController return view('admin/person/view', $data); } - public function create() + public function create(): string { helper(['form']); return view('admin/person/create'); } - public function attemptCreate() + public function attemptCreate(): RedirectResponse { $rules = [ 'image' => @@ -89,7 +94,7 @@ class PersonController extends BaseController return redirect()->route('person-list'); } - public function edit() + public function edit(): string { helper('form'); @@ -101,7 +106,7 @@ class PersonController extends BaseController return view('admin/person/edit', $data); } - public function attemptEdit() + public function attemptEdit(): RedirectResponse { $rules = [ 'image' => @@ -138,7 +143,7 @@ class PersonController extends BaseController return redirect()->route('person-view', [$this->person->id]); } - public function delete() + public function delete(): RedirectResponse { (new PersonModel())->delete($this->person->id); diff --git a/app/Controllers/Admin/PodcastController.php b/app/Controllers/Admin/PodcastController.php index 2fdcdc1b6a..47172c908f 100644 --- a/app/Controllers/Admin/PodcastController.php +++ b/app/Controllers/Admin/PodcastController.php @@ -26,18 +26,17 @@ class PodcastController extends BaseController */ protected $podcast; - /** - * - * @param array<string> $params - * @return static|string - */ - public function _remap(string $method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); } - if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + if ( + ($this->podcast = (new PodcastModel())->getPodcastById( + (int) $params[0], + )) !== null + ) { return $this->$method(); } diff --git a/app/Controllers/Admin/PodcastImportController.php b/app/Controllers/Admin/PodcastImportController.php index 82356daede..75ec683294 100644 --- a/app/Controllers/Admin/PodcastImportController.php +++ b/app/Controllers/Admin/PodcastImportController.php @@ -8,22 +8,21 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; 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\Entities\Image; +use App\Entities\Person; use App\Models\CategoryModel; use App\Models\LanguageModel; use App\Models\PodcastModel; use App\Models\EpisodeModel; use App\Models\PlatformModel; use App\Models\PersonModel; -use App\Models\PodcastPersonModel; -use App\Models\EpisodePersonModel; use Config\Services; use League\HTMLToMarkdown\HtmlConverter; @@ -34,20 +33,20 @@ class PodcastImportController extends BaseController */ protected $podcast; - public function _remap(string $method, string ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); } - if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + if (($this->podcast = (new PodcastModel())->getPodcastById((int) $params[0])) !== null) { return $this->$method(); } throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { helper(['form', 'misc']); @@ -65,7 +64,7 @@ class PodcastImportController extends BaseController return view('admin/podcast/import', $data); } - public function attemptImport() + public function attemptImport(): RedirectResponse { helper(['media', 'misc']); @@ -92,11 +91,11 @@ class PodcastImportController extends BaseController ->withInput() ->with('errors', [ $errorException->getMessage() . - ': <a href="' . - $this->request->getPost('imported_feed_url') . - '" rel="noreferrer noopener" target="_blank">' . - $this->request->getPost('imported_feed_url') . - ' ⎋</a>', + ': <a href="' . + $this->request->getPost('imported_feed_url') . + '" rel="noreferrer noopener" target="_blank">' . + $this->request->getPost('imported_feed_url') . + ' ⎋</a>', ]); } $nsItunes = $feed->channel[0]->children( @@ -151,40 +150,40 @@ class PodcastImportController extends BaseController 'language_code' => $this->request->getPost('language'), 'category_id' => $this->request->getPost('category'), 'parental_advisory' => - $nsItunes->explicit === null - ? null - : (in_array($nsItunes->explicit, ['yes', 'true']) - ? 'explicit' - : (in_array($nsItunes->explicit, ['no', 'false']) - ? 'clean' - : null)), + $nsItunes->explicit === null + ? null + : (in_array($nsItunes->explicit, ['yes', 'true']) + ? 'explicit' + : (in_array($nsItunes->explicit, ['no', 'false']) + ? 'clean' + : null)), 'owner_name' => (string) $nsItunes->owner->name, 'owner_email' => (string) $nsItunes->owner->email, 'publisher' => (string) $nsItunes->author, 'type' => - $nsItunes->type === null ? 'episodic' : $nsItunes->type, + $nsItunes->type === null ? 'episodic' : $nsItunes->type, 'copyright' => (string) $feed->channel[0]->copyright, 'is_blocked' => - $nsItunes->block === null - ? false - : $nsItunes->block === 'yes', + $nsItunes->block === null + ? false + : $nsItunes->block === 'yes', 'is_completed' => - $nsItunes->complete === null - ? false - : $nsItunes->complete === 'yes', + $nsItunes->complete === null + ? false + : $nsItunes->complete === 'yes', 'location_name' => $nsPodcast->location ? (string) $nsPodcast->location : null, 'location_geo' => - !$nsPodcast->location || + !$nsPodcast->location || $nsPodcast->location->attributes()['geo'] === null - ? null - : (string) $nsPodcast->location->attributes()['geo'], + ? null + : (string) $nsPodcast->location->attributes()['geo'], 'location_osm_id' => - !$nsPodcast->location || + !$nsPodcast->location || $nsPodcast->location->attributes()['osm'] === null - ? null - : (string) $nsPodcast->location->attributes()['osm'], + ? null + : (string) $nsPodcast->location->attributes()['osm'], 'created_by' => user_id(), 'updated_by' => user_id(), ]); @@ -194,11 +193,11 @@ class PodcastImportController extends BaseController ->withInput() ->with('errors', [ $ex->getMessage() . - ': <a href="' . - $this->request->getPost('imported_feed_url') . - '" rel="noreferrer noopener" target="_blank">' . - $this->request->getPost('imported_feed_url') . - ' ⎋</a>', + ': <a href="' . + $this->request->getPost('imported_feed_url') . + '" rel="noreferrer noopener" target="_blank">' . + $this->request->getPost('imported_feed_url') . + ' ⎋</a>', ]); } @@ -235,7 +234,7 @@ class PodcastImportController extends BaseController foreach ($platformType['elements'] as $platform) { $platformLabel = $platform->attributes()['platform']; $platformSlug = slugify($platformLabel); - if ($platformModel->getPlatform($platformSlug)) { + if ($platformModel->getPlatform($platformSlug) !== null) { $podcastsPlatformsData[] = [ 'platform_slug' => $platformSlug, 'podcast_id' => $newPodcastId, @@ -255,45 +254,41 @@ class PodcastImportController extends BaseController } foreach ($nsPodcast->person as $podcastPerson) { + $fullName = (string) $podcastPerson; $personModel = new PersonModel(); $newPersonId = null; - if ($newPerson = $personModel->getPerson($podcastPerson)) { + if (($newPerson = $personModel->getPerson($fullName)) !== null) { $newPersonId = $newPerson->id; - } elseif ( - !($newPersonId = $personModel->createPerson( - $podcastPerson, - $podcastPerson->attributes()['href'], - $podcastPerson->attributes()['img'], - )) - ) { - return redirect() - ->back() - ->withInput() - ->with('errors', $personModel->errors()); + } else { + $newPodcastPerson = new Person([ + 'full_name' => $fullName, + 'unique_name' => slugify($fullName), + 'information_url' => $podcastPerson->attributes()['href'], + 'image' => new Image(download_file($podcastPerson->attributes()['img'])), + 'created_by' => user_id(), + 'updated_by' => user_id(), + ]); + + if (!$newPersonId = $personModel->insert($newPodcastPerson)) { + return redirect() + ->back() + ->withInput() + ->with('errors', $personModel->errors()); + } } $personGroup = - $podcastPerson->attributes()['group'] === null - ? ['slug' => ''] - : ReversedTaxonomy::$taxonomy[ - (string) $podcastPerson->attributes()['group'] - ]; + isset($podcastPerson->attributes()['group']) + ? ['slug' => ''] + : ReversedTaxonomy::$taxonomy[(string) $podcastPerson->attributes()['group']]; $personRole = - $podcastPerson->attributes()['role'] === null || + isset($podcastPerson->attributes()['role']) || $personGroup === null - ? ['slug' => ''] - : $personGroup['roles'][ - strval($podcastPerson->attributes()['role']) - ]; - $newPodcastPerson = new PodcastPerson([ - 'podcast_id' => $newPodcastId, - 'person_id' => $newPersonId, - 'person_group' => $personGroup['slug'], - 'person_role' => $personRole['slug'], - ]); - $podcastPersonModel = new PodcastPersonModel(); + ? ['slug' => ''] + : $personGroup['roles'][strval($podcastPerson->attributes()['role'])]; - if (!$podcastPersonModel->insert($newPodcastPerson)) { + $podcastPersonModel = new PersonModel(); + if (!$podcastPersonModel->addPodcastPerson($newPodcastId, $newPersonId, $personGroup['slug'], $personRole['slug'])) { return redirect() ->back() ->withInput() @@ -305,8 +300,8 @@ class PodcastImportController extends BaseController $lastItem = $this->request->getPost('max_episodes') !== null && $this->request->getPost('max_episodes') < $numberItems - ? $this->request->getPost('max_episodes') - : $numberItems; + ? $this->request->getPost('max_episodes') + : $numberItems; $slugs = []; @@ -338,22 +333,12 @@ class PodcastImportController extends BaseController $slug = $slug . '-' . $slugNumber; } $slugs[] = $slug; - - $itemDescriptionHtml = null; - switch ($this->request->getPost('description_field')) { - case 'content': - $itemDescriptionHtml = $nsContent->encoded; - break; - case 'summary': - $itemDescriptionHtml = $nsItunes->summary; - break; - case 'subtitle_summary': - $itemDescriptionHtml = - $nsItunes->subtitle . '<br/>' . $nsItunes->summary; - break; - default: - $itemDescriptionHtml = $item->description; - } + $itemDescriptionHtml = match ($this->request->getPost('description_field')) { + 'content' => $nsContent->encoded, + 'summary' => $nsItunes->summary, + 'subtitle_summary' => $nsItunes->subtitle . '<br/>' . $nsItunes->summary, + default => $item->description, + }; if ( $nsItunes->image !== null && @@ -382,42 +367,42 @@ class PodcastImportController extends BaseController 'description_html' => $itemDescriptionHtml, 'image' => $episodeImage, 'parental_advisory' => - $nsItunes->explicit === null - ? null - : (in_array($nsItunes->explicit, ['yes', 'true']) - ? 'explicit' - : (in_array($nsItunes->explicit, ['no', 'false']) - ? 'clean' - : null)), + $nsItunes->explicit === null + ? null + : (in_array($nsItunes->explicit, ['yes', 'true']) + ? 'explicit' + : (in_array($nsItunes->explicit, ['no', 'false']) + ? 'clean' + : null)), 'number' => - $this->request->getPost('force_renumber') === 'yes' - ? $itemNumber - : $nsItunes->episode, + $this->request->getPost('force_renumber') === 'yes' + ? $itemNumber + : $nsItunes->episode, 'season_number' => - $this->request->getPost('season_number') === null - ? $nsItunes->season - : $this->request->getPost('season_number'), + $this->request->getPost('season_number') === null + ? $nsItunes->season + : $this->request->getPost('season_number'), 'type' => - $nsItunes->episodeType === null - ? 'full' - : $nsItunes->episodeType, + $nsItunes->episodeType === null + ? 'full' + : $nsItunes->episodeType, 'is_blocked' => - $nsItunes->block === null - ? false - : $nsItunes->block === 'yes', + $nsItunes->block === null + ? false + : $nsItunes->block === 'yes', 'location_name' => $nsPodcast->location ? $nsPodcast->location : null, 'location_geo' => - !$nsPodcast->location || + !$nsPodcast->location || $nsPodcast->location->attributes()['geo'] === null - ? null - : $nsPodcast->location->attributes()['geo'], + ? null + : $nsPodcast->location->attributes()['geo'], 'location_osm_id' => - !$nsPodcast->location || + !$nsPodcast->location || $nsPodcast->location->attributes()['osm'] === null - ? null - : $nsPodcast->location->attributes()['osm'], + ? null + : $nsPodcast->location->attributes()['osm'], 'created_by' => user_id(), 'updated_by' => user_id(), 'published_at' => strtotime($item->pubDate), @@ -434,46 +419,40 @@ class PodcastImportController extends BaseController } foreach ($nsPodcast->person as $episodePerson) { + $fullName = (string) $episodePerson; $personModel = new PersonModel(); $newPersonId = null; - if ($newPerson = $personModel->getPerson($episodePerson)) { + if (($newPerson = $personModel->getPerson($fullName)) !== null) { $newPersonId = $newPerson->id; - } elseif ( - !($newPersonId = $personModel->createPerson( - $episodePerson, - $episodePerson->attributes()['href'], - $episodePerson->attributes()['img'], - )) - ) { - return redirect() - ->back() - ->withInput() - ->with('errors', $personModel->errors()); + } else { + $newEpisodePerson = new Person([ + 'full_name' => $fullName, + 'slug' => slugify($fullName), + 'information_url' => $episodePerson->attributes()['href'], + 'image' => new Image(download_file($episodePerson->attributes()['img'])) + ]); + + if (!($newPersonId = $personModel->insert($newEpisodePerson))) { + return redirect() + ->back() + ->withInput() + ->with('errors', $personModel->errors()); + } } $personGroup = $episodePerson->attributes()['group'] === null - ? ['slug' => ''] - : ReversedTaxonomy::$taxonomy[ - strval($episodePerson->attributes()['group']) - ]; + ? ['slug' => ''] + : ReversedTaxonomy::$taxonomy[strval($episodePerson->attributes()['group'])]; $personRole = $episodePerson->attributes()['role'] === null || $personGroup === null - ? ['slug' => ''] - : $personGroup['roles'][ - strval($episodePerson->attributes()['role']) - ]; - $newEpisodePerson = new PodcastPerson([ - 'podcast_id' => $newPodcastId, - 'episode_id' => $newEpisodeId, - 'person_id' => $newPersonId, - 'person_group' => $personGroup['slug'], - 'person_role' => $personRole['slug'], - ]); + ? ['slug' => ''] + : $personGroup['roles'][strval($episodePerson->attributes()['role'])]; + - $episodePersonModel = new EpisodePersonModel(); - if (!$episodePersonModel->insert($newEpisodePerson)) { + $episodePersonModel = new PersonModel(); + if (!$episodePersonModel->addEpisodePerson($newPodcastId, $newEpisodeId, $newPersonId, $personGroup['slug'], $personRole['slug'])) { return redirect() ->back() ->withInput() diff --git a/app/Controllers/Admin/PodcastPersonController.php b/app/Controllers/Admin/PodcastPersonController.php index 894555c2e8..623088a95a 100644 --- a/app/Controllers/Admin/PodcastPersonController.php +++ b/app/Controllers/Admin/PodcastPersonController.php @@ -8,9 +8,9 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use App\Entities\Podcast; use CodeIgniter\Exceptions\PageNotFoundException; -use App\Models\PodcastPersonModel; use App\Models\PodcastModel; use App\Models\PersonModel; @@ -21,13 +21,17 @@ class PodcastPersonController extends BaseController */ protected $podcast; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { throw PageNotFoundException::forPageNotFound(); } - if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + if ( + ($this->podcast = (new PodcastModel())->getPodcastById( + (int) $params[0], + )) !== null + ) { unset($params[0]); return $this->$method(...$params); } @@ -35,13 +39,13 @@ class PodcastPersonController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { helper('form'); $data = [ 'podcast' => $this->podcast, - 'podcastPersons' => (new PodcastPersonModel())->getPodcastPersons( + 'podcastPersons' => (new PersonModel())->getPodcastPersons( $this->podcast->id, ), 'personOptions' => (new PersonModel())->getPersonOptions(), @@ -53,7 +57,7 @@ class PodcastPersonController extends BaseController return view('admin/podcast/person', $data); } - public function attemptAdd() + public function attemptAdd(): RedirectResponse { $rules = [ 'person' => 'required', @@ -66,7 +70,7 @@ class PodcastPersonController extends BaseController ->with('errors', $this->validator->getErrors()); } - (new PodcastPersonModel())->addPodcastPersons( + (new PersonModel())->addPodcastPersons( $this->podcast->id, $this->request->getPost('person'), $this->request->getPost('person_group_role'), @@ -75,9 +79,9 @@ class PodcastPersonController extends BaseController return redirect()->back(); } - public function remove($podcastPersonId) + public function remove(int $podcastPersonId): RedirectResponse { - (new PodcastPersonModel())->removePodcastPersons( + (new PersonModel())->removePodcastPersons( $this->podcast->id, $podcastPersonId, ); diff --git a/app/Controllers/Admin/PodcastPlatformController.php b/app/Controllers/Admin/PodcastPlatformController.php index 27b1deabb9..c68d2a179d 100644 --- a/app/Controllers/Admin/PodcastPlatformController.php +++ b/app/Controllers/Admin/PodcastPlatformController.php @@ -8,6 +8,7 @@ namespace App\Controllers\Admin; +use CodeIgniter\HTTP\RedirectResponse; use App\Entities\Podcast; use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PlatformModel; @@ -21,13 +22,17 @@ class PodcastPlatformController extends BaseController */ protected $podcast; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); } - if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + if ( + ($this->podcast = (new PodcastModel())->getPodcastById( + (int) $params[0], + )) !== null + ) { unset($params[0]); return $this->$method(...$params); } @@ -35,12 +40,12 @@ class PodcastPlatformController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { return view('admin/podcast/platforms/dashboard'); } - public function platforms($platformType) + public function platforms(string $platformType): string { helper('form'); @@ -57,8 +62,9 @@ class PodcastPlatformController extends BaseController return view('admin/podcast/platforms', $data); } - public function attemptPlatformsUpdate($platformType) - { + public function attemptPlatformsUpdate( + string $platformType + ): RedirectResponse { $platformModel = new PlatformModel(); $validation = Services::validation(); @@ -105,8 +111,9 @@ class PodcastPlatformController extends BaseController ->with('message', lang('Platforms.messages.updateSuccess')); } - public function removePodcastPlatform($platformSlug) - { + public function removePodcastPlatform( + string $platformSlug + ): RedirectResponse { (new PlatformModel())->removePodcastPlatform( $this->podcast->id, $platformSlug, diff --git a/app/Controllers/Admin/UserController.php b/app/Controllers/Admin/UserController.php index 3446e28f4f..04b105700c 100644 --- a/app/Controllers/Admin/UserController.php +++ b/app/Controllers/Admin/UserController.php @@ -12,6 +12,7 @@ use CodeIgniter\Exceptions\PageNotFoundException; use App\Authorization\GroupModel; use App\Entities\User; use App\Models\UserModel; +use CodeIgniter\HTTP\RedirectResponse; use Config\Services; class UserController extends BaseController @@ -21,7 +22,7 @@ class UserController extends BaseController */ protected $user; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); @@ -34,14 +35,14 @@ class UserController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function list() + public function list(): string { $data = ['users' => (new UserModel())->findAll()]; return view('admin/user/list', $data); } - public function view() + public function view(): string { $data = ['user' => $this->user]; @@ -49,7 +50,7 @@ class UserController extends BaseController return view('admin/user/view', $data); } - public function create() + public function create(): string { helper('form'); @@ -60,7 +61,7 @@ class UserController extends BaseController return view('admin/user/create', $data); } - public function attemptCreate() + public function attemptCreate(): RedirectResponse { $userModel = new UserModel(); @@ -108,7 +109,7 @@ class UserController extends BaseController ); } - public function edit() + public function edit(): string { helper('form'); @@ -131,7 +132,7 @@ class UserController extends BaseController return view('admin/user/edit', $data); } - public function attemptEdit() + public function attemptEdit(): RedirectResponse { $authorize = Services::authorization(); @@ -149,7 +150,7 @@ class UserController extends BaseController ); } - public function forcePassReset() + public function forcePassReset(): RedirectResponse { $userModel = new UserModel(); $this->user->forcePasswordReset(); @@ -171,7 +172,7 @@ class UserController extends BaseController ); } - public function ban() + public function ban(): RedirectResponse { $authorize = Services::authorization(); if ($authorize->inGroup('superadmin', $this->user->id)) { @@ -204,7 +205,7 @@ class UserController extends BaseController ); } - public function unBan() + public function unBan(): RedirectResponse { $userModel = new UserModel(); $this->user->unBan(); @@ -225,7 +226,7 @@ class UserController extends BaseController ); } - public function delete() + public function delete(): RedirectResponse { $authorize = Services::authorization(); if ($authorize->inGroup('superadmin', $this->user->id)) { diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index 20129e8760..ef5905cddc 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -18,14 +18,14 @@ class AuthController extends MythAuthController * An array of helpers to be automatically loaded * upon class instantiation. * - * @var array + * @var string[] */ protected $helpers = ['components']; /** * Attempt to register a new user. */ - public function attemptRegister() + public function attemptRegister(): RedirectResponse { // Check if registration is allowed if (!$this->config->allowRegistration) { @@ -61,9 +61,9 @@ class AuthController extends MythAuthController ); $user = new User($this->request->getPost($allowedPostFields)); - $this->config->requireActivation !== false - ? $user->generateActivateHash() - : $user->activate(); + $this->config->requireActivation === null + ? $user->activate() + : $user->generateActivateHash(); // Ensure default group gets assigned if set if ($this->config->defaultUserGroup !== null) { @@ -77,7 +77,7 @@ class AuthController extends MythAuthController ->with('errors', $users->errors()); } - if ($this->config->requireActivation !== false) { + if ($this->config->requireActivation !== null) { $activator = service('activator'); $sent = $activator->send($user); @@ -109,7 +109,7 @@ class AuthController extends MythAuthController */ public function attemptReset(): RedirectResponse { - if ($this->config->activeResetter === false) { + if ($this->config->activeResetter === null) { return redirect() ->route('login') ->with('error', lang('Auth.forgotDisabled')); @@ -173,7 +173,7 @@ class AuthController extends MythAuthController ->with('message', lang('Auth.resetSuccess')); } - public function attemptInteractAsActor() + public function attemptInteractAsActor(): RedirectResponse { $rules = [ 'actor_id' => 'required|numeric', diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 7bfe25bca2..5c8909e9ee 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -24,7 +24,7 @@ class BaseController extends Controller * class instantiation. These helpers will be available * to all other controllers that extend BaseController. * - * @var array + * @var string[] */ protected $helpers = ['auth', 'svg', 'components', 'misc']; diff --git a/app/Controllers/EpisodeController.php b/app/Controllers/EpisodeController.php index 55404eae93..ec531e16c0 100644 --- a/app/Controllers/EpisodeController.php +++ b/app/Controllers/EpisodeController.php @@ -8,6 +8,8 @@ namespace App\Controllers; +use CodeIgniter\HTTP\ResponseInterface; +use Config\Services; use Analytics\AnalyticsTrait; use App\Entities\Episode; use App\Entities\Podcast; @@ -30,25 +32,25 @@ class EpisodeController extends BaseController */ protected $episode; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) < 2) { throw PageNotFoundException::forPageNotFound(); } if ( - !($this->podcast = (new PodcastModel())->getPodcastByName( + ($this->podcast = (new PodcastModel())->getPodcastByName( $params[0], - )) + )) === null ) { throw PageNotFoundException::forPageNotFound(); } if ( - $this->episode = (new EpisodeModel())->getEpisodeBySlug( + ($this->episode = (new EpisodeModel())->getEpisodeBySlug( $this->podcast->id, $params[1], - ) + )) !== null ) { unset($params[1]); unset($params[0]); @@ -58,7 +60,7 @@ class EpisodeController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { // Prevent analytics hit when authenticated if (!can_user_interact()) { @@ -71,17 +73,9 @@ class EpisodeController extends BaseController (can_user_interact() ? '_authenticated' : ''); if (!($cachedView = cache($cacheName))) { - helper('persons'); - $episodePersons = []; - construct_person_array($this->episode->persons, $episodePersons); - $podcastPersons = []; - construct_person_array($this->podcast->persons, $podcastPersons); - $data = [ 'podcast' => $this->podcast, 'episode' => $this->episode, - 'episodePersons' => $episodePersons, - 'persons' => $podcastPersons, ]; $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( @@ -91,22 +85,22 @@ class EpisodeController extends BaseController if (can_user_interact()) { helper('form'); return view('podcast/episode_authenticated', $data); - } else { - // The page cache is set to a decade so it is deleted manually upon podcast update - return view('podcast/episode', $data, [ - 'cache' => $secondsToNextUnpublishedEpisode - ? $secondsToNextUnpublishedEpisode - : DECADE, - 'cache_name' => $cacheName, - ]); } + // The page cache is set to a decade so it is deleted manually upon podcast update + return view('podcast/episode', $data, [ + 'cache' => $secondsToNextUnpublishedEpisode + ? $secondsToNextUnpublishedEpisode + : DECADE, + 'cache_name' => $cacheName, + ]); } return $cachedView; } - public function embeddablePlayer($theme = 'light-transparent') - { + public function embeddablePlayer( + string $theme = 'light-transparent' + ): string { header('Content-Security-Policy: frame-ancestors https://* http://*'); // Prevent analytics hit when authenticated @@ -114,7 +108,7 @@ class EpisodeController extends BaseController $this->registerPodcastWebpageHit($this->episode->podcast_id); } - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); if (isset($_SERVER['HTTP_REFERER'])) { $session->set( @@ -152,7 +146,7 @@ class EpisodeController extends BaseController return $cachedView; } - public function oembedJSON() + public function oembedJSON(): ResponseInterface { return $this->response->setJSON([ 'type' => 'rich', @@ -174,7 +168,7 @@ class EpisodeController extends BaseController ]); } - public function oembedXML() + public function oembedXML(): ResponseInterface { $oembed = new SimpleXMLElement( "<?xml version='1.0' encoding='utf-8' standalone='yes'?><oembed></oembed>", diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index f59a5b7fb9..dd8b6d36a5 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -9,20 +9,18 @@ namespace App\Controllers; use App\Models\PodcastModel; +use CodeIgniter\HTTP\RedirectResponse; class HomeController extends BaseController { - /** - * @return RedirectResponse|string - */ - public function index() + public function index(): RedirectResponse|string { $model = new PodcastModel(); $allPodcasts = $model->findAll(); // check if there's only one podcast to redirect user to it - if (count($allPodcasts) == 1) { + if (count($allPodcasts) === 1) { return redirect()->route('podcast-activity', [ $allPodcasts[0]->name, ]); diff --git a/app/Controllers/InstallController.php b/app/Controllers/InstallController.php index 6fb02e0109..4aa8f2b7cf 100644 --- a/app/Controllers/InstallController.php +++ b/app/Controllers/InstallController.php @@ -19,6 +19,7 @@ use Config\Database; use App\Entities\User; use App\Models\UserModel; use CodeIgniter\Controller; +use CodeIgniter\HTTP\RedirectResponse; use Config\Services; use Dotenv\Dotenv; @@ -51,7 +52,14 @@ class InstallController extends Controller public function index(): string { if (!file_exists(ROOTPATH . '.env')) { - $this->createEnv(); + // create empty .env file + try { + $envFile = fopen(ROOTPATH . '.env', 'w'); + fclose($envFile); + } catch (Throwable) { + // Could not create the .env file, redirect to a view with instructions on how to add it manually + return view('install/manual_config'); + } } // Check if .env has all required fields @@ -85,7 +93,7 @@ class InstallController extends Controller try { $dotenv->required('cache.handler'); - } catch (ValidationException $validationException) { + } catch (ValidationException) { return $this->cacheConfig(); } } else { @@ -101,7 +109,7 @@ class InstallController extends Controller 'database.default.DBPrefix', 'cache.handler', ]); - } catch (ValidationException $e) { + } catch (ValidationException) { return view('install/manual_config'); } } @@ -117,7 +125,7 @@ class InstallController extends Controller // if so, show a 404 page throw PageNotFoundException::forPageNotFound(); } - } catch (DatabaseException $databaseException) { + } catch (DatabaseException) { // Could not connect to the database // show database config view to fix value session()->setFlashdata( @@ -137,28 +145,12 @@ class InstallController extends Controller return $this->createSuperAdmin(); } - /** - * Returns the form to generate the .env config file for the instance. - * @return mixed|void - */ - public function createEnv() - { - // create empty .env file - try { - $envFile = fopen(ROOTPATH . '.env', 'w'); - fclose($envFile); - } 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'); - } - } - - public function instanceConfig() + public function instanceConfig(): string { return view('install/instance_config'); } - public function attemptInstanceConfig() + public function attemptInstanceConfig(): RedirectResponse { $rules = [ 'hostname' => 'required|validate_url', @@ -198,12 +190,12 @@ class InstallController extends Controller ); } - public function databaseConfig() + public function databaseConfig(): string { return view('install/database_config'); } - public function attemptDatabaseConfig() + public function attemptDatabaseConfig(): RedirectResponse { $rules = [ 'db_hostname' => 'required', @@ -236,12 +228,12 @@ class InstallController extends Controller return redirect()->back(); } - public function cacheConfig() + public function cacheConfig(): string { return view('install/cache_config'); } - public function attemptCacheConfig() + public function attemptCacheConfig(): RedirectResponse { $rules = [ 'cache_handler' => 'required', @@ -288,7 +280,7 @@ class InstallController extends Controller /** * Returns the form to create a the first superadmin user for the instance. */ - public function createSuperAdmin() + public function createSuperAdmin(): string { return view('install/create_superadmin'); } @@ -298,7 +290,7 @@ class InstallController extends Controller * * After creation, user is redirected to login page to input its credentials. */ - public function attemptCreateSuperAdmin() + public function attemptCreateSuperAdmin(): RedirectResponse { $userModel = new UserModel(); @@ -356,7 +348,7 @@ class InstallController extends Controller * writes config values in .env file * overwrites any existing key and appends new ones * - * @param array $configData key/value config pairs + * @param array<string, string> $configData key/value config pairs */ public static function writeEnv(array $configData): void { @@ -370,7 +362,7 @@ class InstallController extends Controller $keyVal, &$replaced ) { - if (strpos($line, (string) $key) === 0) { + if (str_starts_with($line, (string) $key)) { $replaced = true; return $keyVal; } diff --git a/app/Controllers/NoteController.php b/app/Controllers/NoteController.php index 41e94ae14f..bcf8f09453 100644 --- a/app/Controllers/NoteController.php +++ b/app/Controllers/NoteController.php @@ -8,6 +8,7 @@ namespace App\Controllers; +use CodeIgniter\Exceptions\PageNotFoundException; use ActivityPub\Controllers\NoteController as ActivityPubNoteController; use ActivityPub\Entities\Note as ActivityPubNote; use Analytics\AnalyticsTrait; @@ -34,24 +35,28 @@ class NoteController extends ActivityPubNoteController */ protected $actor; + /** + * @var string[] + */ protected $helpers = ['auth', 'activitypub', 'svg', 'components', 'misc']; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if ( - !($this->podcast = (new PodcastModel())->getPodcastByName( + ($this->podcast = (new PodcastModel())->getPodcastByName( $params[0], - )) + )) === null ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } $this->actor = $this->podcast->actor; - if (count($params) > 1) { - if (!($this->note = model('NoteModel')->getNoteById($params[1]))) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if ( + count($params) > 1 && + !($this->note = model('NoteModel')->getNoteById($params[1])) + ) { + throw PageNotFoundException::forPageNotFound(); } unset($params[0]); unset($params[1]); @@ -77,27 +82,21 @@ class NoteController extends ActivityPubNoteController ); if (!($cachedView = cache($cacheName))) { - helper('persons'); - $persons = []; - construct_person_array($this->podcast->persons, $persons); - $data = [ 'podcast' => $this->podcast, 'actor' => $this->actor, 'note' => $this->note, - 'persons' => $persons, ]; // if user is logged in then send to the authenticated activity view if (can_user_interact()) { helper('form'); return view('podcast/note_authenticated', $data); - } else { - return view('podcast/note', $data, [ - 'cache' => DECADE, - 'cache_name' => $cacheName, - ]); } + return view('podcast/note', $data, [ + 'cache' => DECADE, + 'cache_name' => $cacheName, + ]); } return $cachedView; @@ -129,16 +128,13 @@ class NoteController extends ActivityPubNoteController $episodeUri = $this->request->getPost('episode_url'); if ( $episodeUri && - ($params = extract_params_from_episode_uri(new URI($episodeUri))) + ($params = extract_params_from_episode_uri(new URI($episodeUri))) && + ($episode = (new EpisodeModel())->getEpisodeBySlug( + $params['podcastName'], + $params['episodeSlug'], + )) ) { - if ( - $episode = (new EpisodeModel())->getEpisodeBySlug( - $params['podcastName'], - $params['episodeSlug'], - ) - ) { - $newNote->episode_id = $episode->id; - } + $newNote->episode_id = $episode->id; } $newNote->message = $message; @@ -146,7 +142,7 @@ class NoteController extends ActivityPubNoteController if ( !model('NoteModel')->addNote( $newNote, - $newNote->episode_id ? false : true, + !(bool) $newNote->episode_id, true, ) ) { diff --git a/app/Controllers/PageController.php b/app/Controllers/PageController.php index c2bbd741c7..97d55c6652 100644 --- a/app/Controllers/PageController.php +++ b/app/Controllers/PageController.php @@ -21,7 +21,7 @@ class PageController extends BaseController */ protected $page; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { return $this->$method(); @@ -36,7 +36,7 @@ class PageController extends BaseController throw PageNotFoundException::forPageNotFound(); } - public function index() + public function index(): string { $cacheName = "page-{$this->page->slug}"; if (!($found = cache($cacheName))) { @@ -53,7 +53,7 @@ class PageController extends BaseController return $found; } - public function credits() + public function credits(): string { $locale = service('request')->getLocale(); $allPodcasts = (new PodcastModel())->findAll(); diff --git a/app/Controllers/PodcastController.php b/app/Controllers/PodcastController.php index 4057218a14..f4ae6663eb 100644 --- a/app/Controllers/PodcastController.php +++ b/app/Controllers/PodcastController.php @@ -8,6 +8,7 @@ namespace App\Controllers; +use CodeIgniter\Exceptions\PageNotFoundException; use Analytics\AnalyticsTrait; use App\Entities\Podcast; use App\Models\EpisodeModel; @@ -23,23 +24,25 @@ class PodcastController extends BaseController */ protected $podcast; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (count($params) === 0) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } if ( - $this->podcast = (new PodcastModel())->getPodcastByName($params[0]) + ($this->podcast = (new PodcastModel())->getPodcastByName( + $params[0], + )) !== null ) { unset($params[0]); return $this->$method(...$params); } - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } - public function activity() + public function activity(): string { // Prevent analytics hit when authenticated if (!can_user_interact()) { @@ -58,34 +61,28 @@ class PodcastController extends BaseController ); if (!($cachedView = cache($cacheName))) { - helper('persons'); - $persons = []; - construct_person_array($this->podcast->persons, $persons); - $data = [ 'podcast' => $this->podcast, 'notes' => (new NoteModel())->getActorPublishedNotes( $this->podcast->actor_id, ), - 'persons' => $persons, ]; // if user is logged in then send to the authenticated activity view if (can_user_interact()) { helper('form'); return view('podcast/activity_authenticated', $data); - } else { - return view('podcast/activity', $data, [ - 'cache' => DECADE, - 'cache_name' => $cacheName, - ]); } + return view('podcast/activity', $data, [ + 'cache' => DECADE, + 'cache_name' => $cacheName, + ]); } return $cachedView; } - public function episodes() + public function episodes(): string { // Prevent analytics hit when authenticated if (!can_user_interact()) { @@ -95,7 +92,7 @@ class PodcastController extends BaseController $yearQuery = $this->request->getGet('year'); $seasonQuery = $this->request->getGet('season'); - if (!$yearQuery and !$seasonQuery) { + if (!$yearQuery && !$seasonQuery) { $defaultQuery = (new PodcastModel())->getDefaultQuery( $this->podcast->id, ); @@ -130,7 +127,7 @@ class PodcastController extends BaseController $episodesNavigation = []; $activeQuery = null; foreach ($years as $year) { - $isActive = $yearQuery == $year['year']; + $isActive = $yearQuery === $year['year']; if ($isActive) { $activeQuery = [ 'type' => 'year', @@ -140,7 +137,7 @@ class PodcastController extends BaseController ]; } - array_push($episodesNavigation, [ + $episodesNavigation[] = [ 'label' => $year['year'], 'number_of_episodes' => $year['number_of_episodes'], 'route' => @@ -148,11 +145,11 @@ class PodcastController extends BaseController '?year=' . $year['year'], 'is_active' => $isActive, - ]); + ]; } foreach ($seasons as $season) { - $isActive = $seasonQuery == $season['season_number']; + $isActive = $seasonQuery === $season['season_number']; if ($isActive) { $activeQuery = [ 'type' => 'season', @@ -164,7 +161,7 @@ class PodcastController extends BaseController ]; } - array_push($episodesNavigation, [ + $episodesNavigation[] = [ 'label' => lang('Podcast.season', [ 'seasonNumber' => $season['season_number'], ]), @@ -174,13 +171,9 @@ class PodcastController extends BaseController '?season=' . $season['season_number'], 'is_active' => $isActive, - ]); + ]; } - helper('persons'); - $persons = []; - construct_person_array($this->podcast->persons, $persons); - $data = [ 'podcast' => $this->podcast, 'episodesNav' => $episodesNavigation, @@ -191,7 +184,6 @@ class PodcastController extends BaseController $yearQuery, $seasonQuery, ), - 'persons' => $persons, ]; $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( @@ -201,14 +193,13 @@ class PodcastController extends BaseController // if user is logged in then send to the authenticated episodes view if (can_user_interact()) { return view('podcast/episodes_authenticated', $data); - } else { - return view('podcast/episodes', $data, [ - 'cache' => $secondsToNextUnpublishedEpisode - ? $secondsToNextUnpublishedEpisode - : DECADE, - 'cache_name' => $cacheName, - ]); } + return view('podcast/episodes', $data, [ + 'cache' => $secondsToNextUnpublishedEpisode + ? $secondsToNextUnpublishedEpisode + : DECADE, + 'cache_name' => $cacheName, + ]); } return $cachedView; diff --git a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php index 376877e568..b80ebd16e8 100644 --- a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php +++ b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php @@ -123,7 +123,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder : $city->subdivisions[0]->isoCode; $latitude = round($city->location->latitude, 3); $longitude = round($city->location->longitude, 3); - } catch (AddressNotFoundException $addressNotFoundException) { + } catch (AddressNotFoundException) { //Bad luck, bad IP, nothing to do. } diff --git a/app/Entities/Actor.php b/app/Entities/Actor.php index e19892ddcc..34054745fe 100644 --- a/app/Entities/Actor.php +++ b/app/Entities/Actor.php @@ -18,15 +18,8 @@ use RuntimeException; */ class Actor extends ActivityPubActor { - /** - * @var Podcast|null - */ - protected $podcast; - - /** - * @var boolean - */ - protected $is_podcast; + protected ?Podcast $podcast; + protected bool $is_podcast; public function getIsPodcast(): bool { diff --git a/app/Entities/Category.php b/app/Entities/Category.php index 208a6962f9..a650d65156 100644 --- a/app/Entities/Category.php +++ b/app/Entities/Category.php @@ -21,10 +21,7 @@ use CodeIgniter\Entity\Entity; */ class Category extends Entity { - /** - * @var Category|null - */ - protected $parent; + protected ?Category $parent; /** * @var array<string, string> diff --git a/app/Entities/Credit.php b/app/Entities/Credit.php index 44e0738244..5c6ea62896 100644 --- a/app/Entities/Credit.php +++ b/app/Entities/Credit.php @@ -16,7 +16,7 @@ use CodeIgniter\Entity\Entity; /** * @property int $podcast_id - * @property Podcast $podcast + * @property Podcast|null $podcast * @property int|null $episode_id * @property Episode|null $episode * @property string $full_name @@ -25,34 +25,15 @@ use CodeIgniter\Entity\Entity; * @property string $person_role * @property string $role_label * @property int $person_id - * @property Person $person + * @property Person|null $person */ class Credit extends Entity { - /** - * @var Person - */ - protected $person; - - /** - * @var Podcast - */ - protected $podcast; - - /** - * @var Episode|null - */ - protected $episode; - - /** - * @var string - */ - protected $group_label; - - /** - * @var string - */ - protected $role_label; + protected ?Person $person; + protected ?Podcast $podcast; + protected ?Episode $episode; + protected string $group_label; + protected string $role_label; /** * @var array<string, string> @@ -66,7 +47,7 @@ class Credit extends Entity 'person_role' => 'string', ]; - public function getPerson(): Person + public function getPerson(): ?Person { if ($this->person_id === null) { throw new RuntimeException( @@ -83,7 +64,7 @@ class Credit extends Entity return $this->person; } - public function getPodcast(): Podcast + public function getPodcast(): ?Podcast { if ($this->podcast_id === null) { throw new RuntimeException( diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 5e60ff82c6..178baac572 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -12,8 +12,8 @@ use App\Entities\Location; use App\Libraries\SimpleRSSElement; use App\Models\PodcastModel; use App\Models\SoundbiteModel; -use App\Models\EpisodePersonModel; use App\Models\NoteModel; +use App\Models\PersonModel; use CodeIgniter\Entity\Entity; use CodeIgniter\Files\File; use CodeIgniter\HTTP\Files\UploadedFile; @@ -39,7 +39,7 @@ use RuntimeException; * @property string $audio_file_mimetype * @property int $audio_file_size * @property int $audio_file_header_size - * @property string $description Holds text only description, striped of any markdown or html special characters + * @property string|null $description Holds text only description, striped of any markdown or html special characters * @property string $description_markdown * @property string $description_html * @property Image $image @@ -75,101 +75,43 @@ use RuntimeException; * @property Time $updated_at; * @property Time|null $deleted_at; * - * @property EpisodePerson[] $persons; + * @property Person[] $persons; * @property Soundbite[] $soundbites; * @property string $embeddable_player_url; */ class Episode extends Entity { - /** - * @var Podcast - */ - protected $podcast; - - /** - * @var string - */ - protected $link; - - /** - * @var File - */ - protected $audio_file; - - /** - * @var string - */ - protected $audio_file_url; - - /** - * @var string - */ - protected $audio_file_analytics_url; + protected Podcast $podcast; + protected string $link; + protected File $audio_file; + protected string $audio_file_url; + protected string $audio_file_analytics_url; + protected string $audio_file_web_url; + protected string $audio_file_opengraph_url; + protected string $embeddable_player_url; + protected Image $image; + protected ?string $description; + protected File $transcript_file; + protected File $chapters_file; /** - * @var string + * @var Person[] */ - protected $audio_file_web_url; - - /** - * @var string - */ - protected $audio_file_opengraph_url; - - /** - * @var string - */ - protected $embeddable_player_url; - - /** - * @var Image - */ - protected $image; - - /** - * @var string - */ - protected $description; - - /** - * @var File - */ - protected $transcript_file; - - /** - * @var File - */ - protected $chapters_file; - - /** - * @var EpisodePerson[] - */ - protected $persons; + protected $persons = []; /** * @var Soundbite[] */ - protected $soundbites; + protected $soundbites = []; /** * @var Note[] */ - protected $notes; + protected $notes = []; - /** - * @var Location|null - */ - protected $location; - - /** - * @var string - */ - protected $custom_rss_string; - - /** - * @var string - */ - protected $publication_status; + protected ?Location $location; + protected string $custom_rss_string; + protected string $publication_status; /** * @var string[] @@ -221,10 +163,8 @@ class Episode extends Entity /** * Saves an episode image - * - * @param Image|null $image */ - public function setImage($image = null): self + public function setImage(?Image $image = null): static { if ($image === null) { return $this; @@ -257,10 +197,8 @@ class Episode extends Entity /** * Saves an audio file - * - * @param UploadedFile|File $audioFile */ - public function setAudioFile($audioFile) + public function setAudioFile(UploadedFile|File $audioFile): static { helper(['media', 'id3']); @@ -283,10 +221,8 @@ class Episode extends Entity /** * Saves an episode transcript file - * - * @param UploadedFile|File $transcriptFile */ - public function setTranscriptFile($transcriptFile) + public function setTranscriptFile(UploadedFile|File $transcriptFile): static { helper('media'); @@ -301,10 +237,8 @@ class Episode extends Entity /** * Saves an episode chapters file - * - * @param UploadedFile|File $chaptersFile */ - public function setChaptersFile($chaptersFile) + public function setChaptersFile(UploadedFile|File $chaptersFile): static { helper('media'); @@ -390,9 +324,8 @@ class Episode extends Entity { if ($this->attributes['transcript_file_path']) { return media_base_url($this->attributes['transcript_file_path']); - } else { - return $this->attributes['transcript_file_remote_url']; } + return $this->attributes['transcript_file_remote_url']; } /** @@ -411,7 +344,7 @@ class Episode extends Entity /** * Returns the episode's persons * - * @return EpisodePerson[] + * @return Person[] */ public function getPersons(): array { @@ -422,7 +355,7 @@ class Episode extends Entity } if (empty($this->persons)) { - $this->persons = (new EpisodePersonModel())->getEpisodePersons( + $this->persons = (new PersonModel())->getEpisodePersons( $this->podcast_id, $this->id, ); @@ -483,7 +416,7 @@ class Episode extends Entity ); } - public function getEmbeddablePlayerUrl($theme = null): string + public function getEmbeddablePlayerUrl(string $theme = null): string { return base_url( $theme @@ -501,25 +434,21 @@ class Episode extends Entity ); } - public function setGuid(?string $guid = null) + public function setGuid(?string $guid = null): static { - if ($guid === null) { - $this->attributes['guid'] = $this->getLink(); - } else { - $this->attributes['guid'] = $guid; - } + $this->attributes['guid'] = $guid === null ? $this->getLink() : $guid; return $this; } - public function getPodcast(): Podcast + public function getPodcast(): ?Podcast { return (new PodcastModel())->getPodcastById( $this->attributes['podcast_id'], ); } - public function setDescriptionMarkdown(string $descriptionMarkdown) + public function setDescriptionMarkdown(string $descriptionMarkdown): static { $converter = new CommonMarkConverter([ 'html_input' => 'strip', @@ -563,7 +492,7 @@ class Episode extends Entity if ($this->description === null) { $this->description = trim( preg_replace( - '/\s+/', + '~\s+~', ' ', strip_tags($this->attributes['description_html']), ), @@ -575,11 +504,11 @@ class Episode extends Entity public function getPublicationStatus(): string { - if ($this->publication_status) { + if ($this->publication_status !== '') { return $this->publication_status; } - if (!$this->published_at) { + if ($this->published_at === null) { return 'not_published'; } @@ -594,7 +523,7 @@ class Episode extends Entity /** * Saves the location name and fetches OpenStreetMap info */ - public function setLocation(?string $newLocationName = null) + public function setLocation(?string $newLocationName = null): static { if ($newLocationName === null) { $this->attributes['location_name'] = null; @@ -667,7 +596,7 @@ class Episode extends Entity /** * Saves custom rss tag into json */ - function setCustomRssString(?string $customRssString = null) + function setCustomRssString(?string $customRssString = null): static { if ($customRssString === null) { return $this; @@ -709,19 +638,16 @@ class Episode extends Entity return $partnerLink; } - function getPartnerImageUrl($serviceSlug = null): string + function getPartnerImageUrl(string $serviceSlug = null): string { - $partnerImageUrl = - rtrim($this->getPodcast()->partner_image_url, '/') . - '?pid=' . - $this->getPodcast()->partner_id . - '&guid=' . - urlencode($this->attributes['guid']); - if ($serviceSlug !== null) { - $partnerImageUrl = '&_from=' . $serviceSlug; + return '&_from=' . $serviceSlug; } - return $partnerImageUrl; + return rtrim($this->getPodcast()->partner_image_url, '/') . + '?pid=' . + $this->getPodcast()->partner_id . + '&guid=' . + urlencode($this->attributes['guid']); } } diff --git a/app/Entities/EpisodePerson.php b/app/Entities/EpisodePerson.php deleted file mode 100644 index 9113d353c3..0000000000 --- a/app/Entities/EpisodePerson.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright 2020 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -namespace App\Entities; - -use CodeIgniter\Entity\Entity; -use App\Models\PersonModel; - -/** - * @property int $id - * @property int $podcast_id - * @property int $episode_id - * @property int $person_id - * @property Person $person - * @property string|null $person_group - * @property string|null $person_role - */ -class EpisodePerson extends Entity -{ - /** - * @var Person - */ - protected $person; - - /** - * @var array<string, string> - */ - protected $casts = [ - 'id' => 'integer', - 'podcast_id' => 'integer', - 'episode_id' => 'integer', - 'person_id' => 'integer', - 'person_group' => '?string', - 'person_role' => '?string', - ]; - - public function getPerson(): Person - { - return (new PersonModel())->getPersonById( - $this->attributes['person_id'], - ); - } -} diff --git a/app/Entities/Image.php b/app/Entities/Image.php index 5a9fa82642..74196cad7a 100644 --- a/app/Entities/Image.php +++ b/app/Entities/Image.php @@ -10,7 +10,7 @@ namespace App\Entities; use CodeIgniter\Entity\Entity; use CodeIgniter\Files\File; -use Config\Images as ImagesConfig; +use Config\Images; use Config\Services; use RuntimeException; @@ -35,30 +35,11 @@ use RuntimeException; */ class Image extends Entity { - /** - * @var ImagesConfig - */ - protected $config; - - /** - * @var null|File - */ - protected $file; - - /** - * @var string - */ - protected $dirname; - - /** - * @var string - */ - protected $filename; - - /** - * @var string - */ - protected $extension; + protected Images $config; + protected ?File $file; + protected string $dirname; + protected string $filename; + protected string $extension; public function __construct( ?File $file, diff --git a/app/Entities/Note.php b/app/Entities/Note.php index c6069c6e8b..eeec4e3fdd 100644 --- a/app/Entities/Note.php +++ b/app/Entities/Note.php @@ -9,21 +9,23 @@ namespace App\Entities; use ActivityPub\Entities\Note as ActivityPubNote; -use App\Models\ActorModel; use App\Models\EpisodeModel; use RuntimeException; /** * @property int|null $episode_id * @property Episode|null $episode + * @property Actor $actor + * @property Note $reblog_of_note + * @property Note $reply_to_note */ class Note extends ActivityPubNote { + protected ?Episode $episode; + /** - * @var Episode|null + * @var array<string, string> */ - protected $episode; - protected $casts = [ 'id' => 'string', 'uri' => 'string', @@ -41,10 +43,8 @@ class Note extends ActivityPubNote /** * Returns the note's attached episode - * - * @return \App\Entities\Episode */ - public function getEpisode() + public function getEpisode(): ?Episode { if ($this->episode_id === null) { throw new RuntimeException( diff --git a/app/Entities/Page.php b/app/Entities/Page.php index 500e5bc76b..11c3734e19 100644 --- a/app/Entities/Page.php +++ b/app/Entities/Page.php @@ -25,15 +25,8 @@ use League\CommonMark\CommonMarkConverter; */ class Page extends Entity { - /** - * @var string - */ - protected $link; - - /** - * @var string - */ - protected $content_html; + protected string $link; + protected string $content_html; /** * @var array<string, string> @@ -51,7 +44,7 @@ class Page extends Entity return url_to('page', $this->attributes['slug']); } - public function setContentMarkdown(string $contentMarkdown): self + public function setContentMarkdown(string $contentMarkdown): static { $converter = new CommonMarkConverter([ 'html_input' => 'strip', diff --git a/app/Entities/Person.php b/app/Entities/Person.php index 5946d95eb7..7bfd0b163f 100644 --- a/app/Entities/Person.php +++ b/app/Entities/Person.php @@ -20,13 +20,16 @@ use CodeIgniter\Entity\Entity; * @property string $image_mimetype * @property int $created_by * @property int $updated_by + * @property string|null $group + * @property string|null $role + * @property Podcast|null $podcast + * @property Episode|null $episode */ class Person extends Entity { - /** - * @var Image - */ - protected $image; + protected Image $image; + protected ?Podcast $podcast; + protected ?Episode $episode; /** * @var array<string, string> @@ -38,6 +41,10 @@ class Person extends Entity 'information_url' => '?string', 'image_path' => 'string', 'image_mimetype' => 'string', + 'podcast_id' => '?integer', + 'episode_id' => '?integer', + 'group' => '?string', + 'role' => '?string', 'created_by' => 'integer', 'updated_by' => 'integer', ]; @@ -45,7 +52,7 @@ class Person extends Entity /** * Saves a picture in `public/media/persons/` */ - public function setImage(Image $image): self + public function setImage(Image $image): static { helper('media'); diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index 7476e20d81..7b84d3df9f 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -11,8 +11,8 @@ namespace App\Entities; use App\Libraries\SimpleRSSElement; use App\Models\CategoryModel; use App\Models\EpisodeModel; +use App\Models\PersonModel; use App\Models\PlatformModel; -use App\Models\PodcastPersonModel; use CodeIgniter\Entity\Entity; use App\Models\UserModel; use CodeIgniter\I18n\Time; @@ -22,7 +22,7 @@ use RuntimeException; /** * @property int $id * @property int $actor_id - * @property Actor $actor + * @property Actor|null $actor * @property string $name * @property string $link * @property string $feed_url @@ -35,7 +35,7 @@ use RuntimeException; * @property string $image_mimetype * @property string $language_code * @property int $category_id - * @property Category $category + * @property Category|null $category * @property int[] $other_categories_ids * @property Category[] $other_categories * @property string|null $parental_advisory @@ -68,7 +68,7 @@ use RuntimeException; * @property Time|null $deleted_at; * * @property Episode[] $episodes - * @property PodcastPerson[] $persons + * @property Person[] $persons * @property User[] $contributors * @property Platform[] $podcasting_platforms * @property Platform[] $social_platforms @@ -77,80 +77,54 @@ use RuntimeException; */ class Podcast extends Entity { - /** - * @var string - */ - protected $link; - - /** - * @var Actor - */ - protected $actor; - - /** - * @var Image - */ - protected $image; - - /** - * @var string - */ - protected $description; - - /** - * @var Category - */ - protected $category; + protected string $link; + protected ?Actor $actor; + protected Image $image; + protected string $description; + protected ?Category $category; /** * @var Category[] */ - protected $other_categories; + protected $other_categories = []; /** * @var string[] */ - protected $other_categories_ids; + protected $other_categories_ids = []; /** * @var Episode[] */ - protected $episodes; + protected $episodes = []; /** - * @var PodcastPerson[] + * @var Person[] */ - protected $persons; + protected $persons = []; /** * @var User[] */ - protected $contributors; + protected $contributors = []; /** * @var Platform[] */ - protected $podcasting_platforms; + protected $podcasting_platforms = []; /** * @var Platform[] */ - protected $social_platforms; + protected $social_platforms = []; /** * @var Platform[] */ - protected $funding_platforms; + protected $funding_platforms = []; - /** - * @var Location|null - */ - protected $location; - - /** - * @var string - */ - protected $custom_rss_string; + protected ?Location $location; + protected string $custom_rss_string; /** * @var array<string, string> @@ -193,7 +167,7 @@ class Podcast extends Entity public function getActor(): Actor { - if (!$this->actor_id) { + if ($this->actor_id === 0) { throw new RuntimeException( 'Podcast must have an actor_id before getting actor.', ); @@ -208,10 +182,8 @@ class Podcast extends Entity /** * Saves a cover image to the corresponding podcast folder in `public/media/podcast_name/` - * - * @param Image $image */ - public function setImage($image): self + public function setImage(Image $image): static { // Save image $image->saveImage('podcasts/' . $this->attributes['name'], 'cover'); @@ -263,7 +235,7 @@ class Podcast extends Entity /** * Returns the podcast's persons * - * @return PodcastPerson[] + * @return Person[] */ public function getPersons(): array { @@ -274,9 +246,7 @@ class Podcast extends Entity } if (empty($this->persons)) { - $this->persons = (new PodcastPersonModel())->getPodcastPersons( - $this->id, - ); + $this->persons = (new PersonModel())->getPodcastPersons($this->id); } return $this->persons; @@ -284,18 +254,16 @@ class Podcast extends Entity /** * Returns the podcast category entity - * - * @return Category */ - public function getCategory(): Category + public function getCategory(): ?Category { - if (empty($this->id)) { + if ($this->id === null) { throw new RuntimeException( 'Podcast must be created before getting category.', ); } - if (empty($this->category)) { + if ($this->category === null) { $this->category = (new CategoryModel())->getCategoryById( $this->category_id, ); @@ -326,7 +294,7 @@ class Podcast extends Entity return $this->contributors; } - public function setDescriptionMarkdown(string $descriptionMarkdown): self + public function setDescriptionMarkdown(string $descriptionMarkdown): static { $converter = new CommonMarkConverter([ 'html_input' => 'strip', @@ -343,7 +311,7 @@ class Podcast extends Entity public function setEpisodeDescriptionFooterMarkdown( ?string $episodeDescriptionFooterMarkdown = null - ): self { + ): static { if ($episodeDescriptionFooterMarkdown) { $converter = new CommonMarkConverter([ 'html_input' => 'strip', @@ -363,13 +331,13 @@ class Podcast extends Entity public function getDescription(): string { - if ($this->description) { + if ($this->description !== '') { return $this->description; } return trim( preg_replace( - '/\s+/', + '~\s+~', ' ', strip_tags($this->attributes['description_html']), ), @@ -483,7 +451,7 @@ class Podcast extends Entity /** * Saves the location name and fetches OpenStreetMap info */ - public function setLocation(?string $newLocationName = null) + public function setLocation(?string $newLocationName = null): static { if ($newLocationName === null) { $this->attributes['location_name'] = null; @@ -529,8 +497,6 @@ class Podcast extends Entity /** * Get custom rss tag as XML String - * - * @return string */ function getCustomRssString(): string { @@ -555,10 +521,8 @@ class Podcast extends Entity /** * Saves custom rss tag into json - * - * @param string $customRssString */ - function setCustomRssString($customRssString): self + function setCustomRssString(string $customRssString): static { if (empty($customRssString)) { return $this; diff --git a/app/Entities/PodcastPerson.php b/app/Entities/PodcastPerson.php deleted file mode 100644 index a3f06aecd3..0000000000 --- a/app/Entities/PodcastPerson.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright 2020 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -namespace App\Entities; - -use CodeIgniter\Entity\Entity; -use App\Models\PersonModel; - -/** - * @property int $id - * @property int $podcast_id - * @property int $person_id - * @property Person $person - * @property string|null $person_group - * @property string|null $person_role - */ -class PodcastPerson extends Entity -{ - /** - * @var Person - */ - protected $person; - - /** - * @var array<string, string> - */ - protected $casts = [ - 'id' => 'integer', - 'podcast_id' => 'integer', - 'person_id' => 'integer', - 'person_group' => '?string', - 'person_role' => '?string', - ]; - - public function getPerson(): ?Person - { - return (new PersonModel())->getPersonById( - $this->attributes['person_id'], - ); - } -} diff --git a/app/Filters/PermissionFilter.php b/app/Filters/PermissionFilter.php index f4142387f8..84495dc38c 100644 --- a/app/Filters/PermissionFilter.php +++ b/app/Filters/PermissionFilter.php @@ -21,7 +21,7 @@ class PermissionFilter implements FilterInterface * sent back to the client, allowing for error pages, * redirects, etc. * - * @param array|null $params + * @param string[]|null $params * @return void|mixed */ public function before(RequestInterface $request, $params = null) @@ -50,8 +50,8 @@ class PermissionFilter implements FilterInterface foreach ($params as $permission) { // check if permission is for a specific podcast if ( - (startsWith($permission, 'podcast-') || - startsWith($permission, 'podcast_episodes-')) && + (str_starts_with($permission, 'podcast-') || + str_starts_with($permission, 'podcast_episodes-')) && count($routerParams) > 0 ) { if ( @@ -91,7 +91,7 @@ class PermissionFilter implements FilterInterface * to stop execution of other after filters, short of * throwing an Exception or Error. * - * @param array|null $arguments + * @param string[]|null $arguments */ public function after( RequestInterface $request, diff --git a/app/Helpers/auth_helper.php b/app/Helpers/auth_helper.php index b390d51626..4202b3df1a 100644 --- a/app/Helpers/auth_helper.php +++ b/app/Helpers/auth_helper.php @@ -27,7 +27,7 @@ if (!function_exists('set_interact_as_actor')) { /** * Sets the actor id of which the user is acting as */ - function set_interact_as_actor($actorId): void + function set_interact_as_actor(int $actorId): void { $authenticate = Services::authentication(); $authenticate->check(); @@ -65,10 +65,8 @@ if (!function_exists('interact_as_actor_id')) { if (!function_exists('interact_as_actor')) { /** * Get the actor the user is currently interacting as - * - * @return Actor|false */ - function interact_as_actor() + function interact_as_actor(): Actor|false { $authenticate = Services::authentication(); $authenticate->check(); diff --git a/app/Helpers/breadcrumb_helper.php b/app/Helpers/breadcrumb_helper.php index 503ee07711..5be34556f1 100644 --- a/app/Helpers/breadcrumb_helper.php +++ b/app/Helpers/breadcrumb_helper.php @@ -23,7 +23,10 @@ if (!function_exists('render_breadcrumb')) { } if (!function_exists('replace_breadcrumb_params')) { - function replace_breadcrumb_params($newParams): void + /** + * @param string[] $newParams + */ + function replace_breadcrumb_params(array $newParams): void { $breadcrumb = Services::breadcrumb(); $breadcrumb->replaceParams($newParams); diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php index a156d3fdab..647bfe2031 100644 --- a/app/Helpers/components_helper.php +++ b/app/Helpers/components_helper.php @@ -16,10 +16,8 @@ if (!function_exists('button')) { * * Creates a stylized button or button like anchor tag if the URL is defined. * - * @param array $customOptions button options: variant, size, iconLeft, iconRight - * @param array $customAttributes Additional attributes - * - * @return string + * @param array<string, string|null|bool> $customOptions button options: variant, size, iconLeft, iconRight + * @param array<string, string> $customAttributes Additional attributes */ function button( string $label = '', @@ -130,10 +128,8 @@ if (!function_exists('icon_button')) { * * @param string $icon The button icon * @param string $title The button label - * @param array $customOptions button options: variant, size, iconLeft, iconRight - * @param array $customAttributes Additional attributes - * - * @return string + * @param array<string, string|null|bool> $customOptions button options: variant, size, iconLeft, iconRight + * @param array<string, string> $customAttributes Additional attributes */ function icon_button( string $icon, @@ -167,8 +163,6 @@ if (!function_exists('hint_tooltip')) { * Used to produce tooltip with a question mark icon for hint texts * * @param string $hintText The hint text - * - * @return string */ function hint_tooltip(string $hintText = '', string $class = ''): string { @@ -193,11 +187,9 @@ if (!function_exists('data_table')) { * * Creates a stylized table. * - * @param array $columns array of associate arrays with `header` and `cell` keys where `cell` is a function with a row of $data as parameter - * @param array $data data to loop through and display in rows - * @param array ...$rest Any other argument to pass to the `cell` function - * - * @return string + * @param array<array<string, mixed>> $columns array of associate arrays with `header` and `cell` keys where `cell` is a function with a row of $data as parameter + * @param mixed[] $data data to loop through and display in rows + * @param mixed ...$rest Any other argument to pass to the `cell` function */ function data_table(array $columns, array $data = [], ...$rest): string { @@ -252,8 +244,6 @@ if (!function_exists('publication_pill')) { * Publication pill component * * Shows the stylized publication datetime in regards to current datetime. - * - * @return string */ function publication_pill( ?Time $publicationDate, @@ -303,7 +293,6 @@ if (!function_exists('publication_button')) { * Displays the appropriate publication button depending on the publication status. * * @param boolean $publicationStatus the episode's publication status * - * @return string */ function publication_button( int $podcastId, diff --git a/app/Helpers/form_helper.php b/app/Helpers/form_helper.php index afa928f283..180efd5e43 100644 --- a/app/Helpers/form_helper.php +++ b/app/Helpers/form_helper.php @@ -15,9 +15,7 @@ if (!function_exists('form_section')) { * * @param string $title The section title * @param string $subtitle The section subtitle - * @param array $attributes Additional attributes - * - * @return string + * @param array<string, string> $attributes Additional attributes */ function form_section( string $title = '', @@ -54,9 +52,7 @@ if (!function_exists('form_section_close')) { /** * Form Section close Tag * - * @param string $extra * - * @return string */ function form_section_close(string $extra = ''): string { @@ -72,10 +68,11 @@ if (!function_exists('form_switch')) { * * Abstracts form_label to stylize it as a switch toggle * - * @return string + * @param mixed[] $data + * @param mixed[] $extra */ function form_switch( - $label = '', + string $label = '', array $data = [], string $value = '', bool $checked = false, @@ -104,11 +101,9 @@ if (!function_exists('form_label')) { * * @param string $label_text The text to appear onscreen * @param string $id The id the label applies to - * @param array $attributes Additional attributes + * @param array<string, string> $attributes Additional attributes * @param string $hintText Hint text to add next to the label * @param boolean $isOptional adds an optional text if true - * - * @return string */ function form_label( string $label_text = '', @@ -151,7 +146,9 @@ if (!function_exists('form_multiselect')) { /** * Multi-select menu * - * @return string + * @param array<string, string> $options + * @param string[] $selected + * @param array<string, string> $customExtra */ function form_multiselect( string $name = '', diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php index 174dea022c..b0739c6c73 100644 --- a/app/Helpers/id3_helper.php +++ b/app/Helpers/id3_helper.php @@ -68,7 +68,7 @@ if (!function_exists('write_audio_file_tags')) { ], 'album' => [$episode->podcast->title], 'year' => [ - $episode->published_at + $episode->published_at !== null ? $episode->published_at->format('Y') : '', ], diff --git a/app/Helpers/location_helper.php b/app/Helpers/location_helper.php index 1c3701d9b3..9a91e8a1d5 100644 --- a/app/Helpers/location_helper.php +++ b/app/Helpers/location_helper.php @@ -11,6 +11,10 @@ use Config\Services; if (!function_exists('fetch_osm_location')) { /** * Fetches places from Nominatim OpenStreetMap + * + * TODO: move this to Location object? + * + * @return array<string, string>|null */ function fetch_osm_location(string $locationName): ?array { diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php index 8491a9642c..c644827a7a 100644 --- a/app/Helpers/media_helper.php +++ b/app/Helpers/media_helper.php @@ -20,7 +20,7 @@ if (!function_exists('save_media')) { function save_media( File $file, string $folder = '', - string $filename + string $filename = '' ): string { if (($extension = $file->getExtension()) !== '') { $filename = $filename . '.' . $extension; @@ -91,9 +91,9 @@ 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 + * @param string|string[] $uri URI string or array of URI segments */ - function media_path($uri = ''): string + function media_path(string|array $uri = ''): string { // convert segment array to string if (is_array($uri)) { @@ -109,9 +109,9 @@ if (!function_exists('media_base_url')) { /** * Return the media base URL to use in views * - * @param string|string[] $uri URI string or array of URI segments + * @param string|string[] $uri URI string or array of URI segments */ - function media_base_url($uri = ''): string + function media_base_url(string|array $uri = ''): string { // convert segment array to string if (is_array($uri)) { diff --git a/app/Helpers/misc_helper.php b/app/Helpers/misc_helper.php index 91983d48f3..d8d4e8a29e 100644 --- a/app/Helpers/misc_helper.php +++ b/app/Helpers/misc_helper.php @@ -23,23 +23,9 @@ if (!function_exists('get_browser_language')) { } } -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; - } -} - if (!function_exists('slugify')) { - function slugify($text) + function slugify(string $text): string { - if (empty($text)) { - return 'n-a'; - } - // replace non letter or digits by - $text = preg_replace('~[^\pL\d]+~u', '-', $text); diff --git a/app/Helpers/persons_helper.php b/app/Helpers/persons_helper.php deleted file mode 100644 index c7863d4a1d..0000000000 --- a/app/Helpers/persons_helper.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright 2021 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -use App\Entities\Person; -use App\Entities\EpisodePerson; -use App\Entities\PodcastPerson; - -if (!function_exists('construct_person_array')) { - /** - * Fetches persons from an episode - * - * @param Person[]|PodcastPerson[]|EpisodePerson[] $persons - */ - function construct_person_array(array $persons, array &$personsArray): void - { - foreach ($persons as $person) { - if (array_key_exists($person->id, $personsArray)) { - $personsArray[$person->id]['roles'] .= - empty($person->person_group) || empty($person->person_role) - ? '' - : (empty($personsArray[$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 498fee97c1..41adc5460d 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -187,19 +187,19 @@ if (!function_exists('get_rss_feed')) { foreach ($podcast->persons as $podcastPerson) { $podcastPersonElement = $channel->addChild( 'person', - htmlspecialchars($podcastPerson->person->full_name), + htmlspecialchars($podcastPerson->full_name), $podcast_namespace, ); if ( - $podcastPerson->person_role !== null && - $podcastPerson->person_group !== null + $podcastPerson->role !== null && + $podcastPerson->role !== null ) { $podcastPersonElement->addAttribute( 'role', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label", + "PersonsTaxonomy.persons.{$podcastPerson->group}.roles.{$podcastPerson->role}.label", [], 'en', ), @@ -207,27 +207,28 @@ if (!function_exists('get_rss_feed')) { ); } - if ($podcastPerson->person_group !== null) { + if ($podcastPerson->group !== null) { $podcastPersonElement->addAttribute( 'group', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label", + "PersonsTaxonomy.persons.{$podcastPerson->group}.label", [], 'en', ), ), ); } + $podcastPersonElement->addAttribute( 'img', - $podcastPerson->person->image->large_url, + $podcastPerson->image->large_url, ); - if ($podcastPerson->person->information_url !== null) { + if ($podcastPerson->information_url !== null) { $podcastPersonElement->addAttribute( 'href', - $podcastPerson->person->information_url, + $podcastPerson->information_url, ); } } @@ -417,18 +418,18 @@ if (!function_exists('get_rss_feed')) { foreach ($episode->persons as $episodePerson) { $episodePersonElement = $item->addChild( 'person', - htmlspecialchars($episodePerson->person->full_name), + htmlspecialchars($episodePerson->full_name), $podcast_namespace, ); if ( - !empty($episodePerson->person_role) && - !empty($episodePerson->person_group) + !empty($episodePerson->role) && + !empty($episodePerson->group) ) { $episodePersonElement->addAttribute( 'role', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label", + "PersonsTaxonomy.persons.{$episodePerson->group}.roles.{$episodePerson->role}.label", [], 'en', ), @@ -440,7 +441,7 @@ if (!function_exists('get_rss_feed')) { 'group', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$episodePerson->person_group}.label", + "PersonsTaxonomy.persons.{$episodePerson->group}.label", [], 'en', ), @@ -449,12 +450,12 @@ if (!function_exists('get_rss_feed')) { } $episodePersonElement->addAttribute( 'img', - $episodePerson->person->image->large_url, + $episodePerson->image->large_url, ); - if (!empty($episodePerson->person->information_url)) { + if (!empty($episodePerson->information_url)) { $episodePersonElement->addAttribute( 'href', - $episodePerson->person->information_url, + $episodePerson->information_url, ); } } @@ -512,10 +513,11 @@ if (!function_exists('rss_to_array')) { /** * Converts XML to array * - * FIXME: should be SimpleRSSElement - * @param SimpleXMLElement $xmlNode + * FIXME: param should be SimpleRSSElement + * + * @return array<string, mixed> */ - function rss_to_array(SimpleXMLElement $xmlNode): array + function rss_to_array(SimpleXMLElement $rssNode): array { $nameSpaces = [ '', @@ -523,17 +525,17 @@ if (!function_exists('rss_to_array')) { '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['name'] = $rssNode->getName(); + $arrayNode['namespace'] = $rssNode->getNamespaces(false); + foreach ($rssNode->attributes() as $key => $value) { $arrayNode['attributes'][$key] = (string) $value; } - $textcontent = trim((string) $xmlNode); + $textcontent = trim((string) $rssNode); if (strlen($textcontent) > 0) { $arrayNode['content'] = $textcontent; } foreach ($nameSpaces as $currentNameSpace) { - foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) { + foreach ($rssNode->children($currentNameSpace) as $childXmlNode) { $arrayNode['elements'][] = rss_to_array($childXmlNode); } } @@ -546,10 +548,13 @@ if (!function_exists('array_to_rss')) { /** * Inserts array (converted to XML node) in XML node * + * @param array<string, mixed> $arrayNode * @param SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached */ - function array_to_rss(array $arrayNode, SimpleRSSElement &$xmlNode) - { + function array_to_rss( + array $arrayNode, + SimpleRSSElement &$xmlNode + ): SimpleRSSElement { if (array_key_exists('elements', $arrayNode)) { foreach ($arrayNode['elements'] as $childArrayNode) { $childXmlNode = $xmlNode->addChild( diff --git a/app/Helpers/url_helper.php b/app/Helpers/url_helper.php index 80b4f7b562..f92d34b0b8 100644 --- a/app/Helpers/url_helper.php +++ b/app/Helpers/url_helper.php @@ -47,7 +47,9 @@ if (!function_exists('current_season_url')) { if (!function_exists('extract_params_from_episode_uri')) { /** - * Returns podcast name and episode slug from episode string uri + * Returns podcast name and episode slug from episode string + * + * @return array<string, string>|null */ function extract_params_from_episode_uri(URI $episodeUri): ?array { diff --git a/app/Libraries/ActivityPub/Activities/AnnounceActivity.php b/app/Libraries/ActivityPub/Activities/AnnounceActivity.php index 30dcf4d292..e96508982c 100644 --- a/app/Libraries/ActivityPub/Activities/AnnounceActivity.php +++ b/app/Libraries/ActivityPub/Activities/AnnounceActivity.php @@ -14,6 +14,7 @@ namespace ActivityPub\Activities; use ActivityPub\Core\Activity; +use ActivityPub\Entities\Note; class AnnounceActivity extends Activity { @@ -22,7 +23,7 @@ class AnnounceActivity extends Activity */ protected $type = 'Announce'; - public function __construct($reblogNote) + public function __construct(Note $reblogNote) { $this->actor = $reblogNote->actor->uri; $this->object = $reblogNote->reblog_of_note->uri; diff --git a/app/Libraries/ActivityPub/ActivityRequest.php b/app/Libraries/ActivityPub/ActivityRequest.php index 9b3accbdaf..f2c88e09da 100644 --- a/app/Libraries/ActivityPub/ActivityRequest.php +++ b/app/Libraries/ActivityPub/ActivityRequest.php @@ -34,7 +34,7 @@ class ActivityRequest protected $activity; /** - * @var array + * @var array<string, string[]> */ protected $options = [ 'headers' => [ @@ -71,7 +71,7 @@ class ActivityRequest ($this->uri->getPort() ? ':' . $this->uri->getPort() : ''); } - public function sign($keyId, $privateKey): void + public function sign(string $keyId, string $privateKey): void { $rsa = new RSA(); $rsa->loadKey($privateKey); // private key diff --git a/app/Libraries/ActivityPub/Config/ActivityPub.php b/app/Libraries/ActivityPub/Config/ActivityPub.php index 3ac4e4fc67..47ae7d3e9b 100644 --- a/app/Libraries/ActivityPub/Config/ActivityPub.php +++ b/app/Libraries/ActivityPub/Config/ActivityPub.php @@ -8,6 +8,8 @@ namespace ActivityPub\Config; +use ActivityPub\Objects\ActorObject; +use ActivityPub\Objects\NoteObject; use CodeIgniter\Config\BaseConfig; class ActivityPub extends BaseConfig @@ -18,12 +20,12 @@ class ActivityPub extends BaseConfig * -------------------------------------------------------------------- * @var string */ - public $actorObject = 'ActivityPub\Objects\ActorObject'; + public $actorObject = ActorObject::class; /** * @var string */ - public $noteObject = 'ActivityPub\Objects\NoteObject'; + public $noteObject = NoteObject::class; /** * -------------------------------------------------------------------- diff --git a/app/Libraries/ActivityPub/Controllers/ActorController.php b/app/Libraries/ActivityPub/Controllers/ActorController.php index 4bc7b77e85..671fa56a00 100644 --- a/app/Libraries/ActivityPub/Controllers/ActorController.php +++ b/app/Libraries/ActivityPub/Controllers/ActorController.php @@ -41,7 +41,7 @@ class ActorController extends Controller $this->config = config('ActivityPub'); } - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if ( count($params) > 0 && @@ -301,10 +301,7 @@ class ActorController extends Controller ->setBody($followersCollection->toJSON()); } - /** - * @return mixed|ResponseInterface - */ - public function attemptFollow() + public function attemptFollow(): RedirectResponse|ResponseInterface { $rules = [ 'handle' => @@ -354,7 +351,7 @@ class ActorController extends Controller ); } - public function activity($activityId): RedirectResponse + public function activity(string $activityId): RedirectResponse { if ( !($activity = model('ActivityModel')->getActivityById($activityId)) diff --git a/app/Libraries/ActivityPub/Controllers/BlockController.php b/app/Libraries/ActivityPub/Controllers/BlockController.php index 22c2e1484a..ef8f1d8938 100644 --- a/app/Libraries/ActivityPub/Controllers/BlockController.php +++ b/app/Libraries/ActivityPub/Controllers/BlockController.php @@ -8,6 +8,7 @@ namespace ActivityPub\Controllers; +use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\Controller; class BlockController extends Controller @@ -17,7 +18,7 @@ class BlockController extends Controller */ protected $helpers = ['activitypub']; - public function attemptBlockActor() + public function attemptBlockActor(): RedirectResponse { $rules = [ 'handle' => 'required', @@ -51,7 +52,7 @@ class BlockController extends Controller return redirect()->back(); } - function attemptBlockDomain() + function attemptBlockDomain(): RedirectResponse { $rules = [ 'domain' => 'required', @@ -71,7 +72,7 @@ class BlockController extends Controller return redirect()->back(); } - function attemptUnblockActor() + function attemptUnblockActor(): RedirectResponse { $rules = [ 'actor_id' => 'required', @@ -89,7 +90,7 @@ class BlockController extends Controller return redirect()->back(); } - function attemptUnblockDomain() + function attemptUnblockDomain(): RedirectResponse { $rules = [ 'domain' => 'required', diff --git a/app/Libraries/ActivityPub/Controllers/NoteController.php b/app/Libraries/ActivityPub/Controllers/NoteController.php index 4883acc650..bea02669ef 100644 --- a/app/Libraries/ActivityPub/Controllers/NoteController.php +++ b/app/Libraries/ActivityPub/Controllers/NoteController.php @@ -41,7 +41,7 @@ class NoteController extends Controller $this->config = config('ActivityPub'); } - public function _remap(string $method, string ...$params) + public function _remap(string $method, string ...$params): mixed { if (!($this->note = model('NoteModel')->getNoteById($params[0]))) { throw PageNotFoundException::forPageNotFound(); @@ -63,8 +63,7 @@ class NoteController extends Controller public function replies(): RedirectResponse { - /** get note replies - * @var NoteModel */ + /** get note replies */ $noteReplies = model('NoteModel') ->where( 'in_reply_to_id', @@ -216,10 +215,7 @@ class NoteController extends Controller return redirect()->back(); } - /** - * @return mixed|ResponseInterface - */ - public function attemptRemoteAction(string $action) + public function attemptRemoteAction(string $action): RedirectResponse|ResponseInterface { $rules = [ 'handle' => diff --git a/app/Libraries/ActivityPub/Controllers/WebFingerController.php b/app/Libraries/ActivityPub/Controllers/WebFingerController.php index 0ec6f92ab7..136232cb0b 100644 --- a/app/Libraries/ActivityPub/Controllers/WebFingerController.php +++ b/app/Libraries/ActivityPub/Controllers/WebFingerController.php @@ -20,7 +20,7 @@ class WebFingerController extends Controller { try { $webfinger = new WebFinger($this->request->getGet('resource')); - } catch (Exception $exception) { + } catch (Exception) { // return 404, actor not found throw PageNotFoundException::forPageNotFound(); } diff --git a/app/Libraries/ActivityPub/Core/AbstractObject.php b/app/Libraries/ActivityPub/Core/AbstractObject.php index aeca7c4a48..fa366e7de4 100644 --- a/app/Libraries/ActivityPub/Core/AbstractObject.php +++ b/app/Libraries/ActivityPub/Core/AbstractObject.php @@ -18,7 +18,7 @@ abstract class AbstractObject /** * @param mixed $value */ - public function set(string $property, $value): self + public function set(string $property, $value): static { $this->$property = $value; @@ -49,10 +49,7 @@ abstract class AbstractObject }); } - /** - * @return string|bool - */ - public function toJSON() + public function toJSON(): string|bool { return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE); } diff --git a/app/Libraries/ActivityPub/Core/ObjectType.php b/app/Libraries/ActivityPub/Core/ObjectType.php index 5ef6ddcd18..caf64e6d33 100644 --- a/app/Libraries/ActivityPub/Core/ObjectType.php +++ b/app/Libraries/ActivityPub/Core/ObjectType.php @@ -16,7 +16,7 @@ namespace ActivityPub\Core; class ObjectType extends AbstractObject { /** - * @var array|string + * @var string|string[] */ protected $context = 'https://www.w3.org/ns/activitystreams'; @@ -41,12 +41,12 @@ class ObjectType extends AbstractObject protected $published; /** - * @var array + * @var string[] */ protected $to = ['https://www.w3.org/ns/activitystreams#Public']; /** - * @var array + * @var string[] */ protected $cc = []; } diff --git a/app/Libraries/ActivityPub/Entities/Note.php b/app/Libraries/ActivityPub/Entities/Note.php index 9d68ef6cf4..484fc3dddb 100644 --- a/app/Libraries/ActivityPub/Entities/Note.php +++ b/app/Libraries/ActivityPub/Entities/Note.php @@ -243,7 +243,7 @@ class Note extends UuidEntity return $this->reblog_of_note; } - public function setMessage(string $message): self + public function setMessage(string $message): static { helper('activitypub'); diff --git a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php index 325035432e..7355941722 100644 --- a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php +++ b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php @@ -23,7 +23,7 @@ class ActivityPubFilter implements FilterInterface * sent back to the client, allowing for error pages, * redirects, etc. * - * @param array|null $params + * @param string[]|null $params * @return void|mixed */ public function before(RequestInterface $request, $params = null) @@ -67,7 +67,7 @@ class ActivityPubFilter implements FilterInterface try { // securityCheck: check activity signature before handling it (new HttpSignature())->verify(); - } catch (Exception $exception) { + } catch (Exception) { // Invalid HttpSignature (401 = unauthorized) // TODO: show error message? return service('response')->setStatusCode(401); @@ -82,7 +82,7 @@ class ActivityPubFilter implements FilterInterface * to stop execution of other after filters, short of * throwing an Exception or Error. * - * @param array|null $arguments + * @param string[]|null $arguments */ public function after( RequestInterface $request, diff --git a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php index ed7931ca02..53df425dfc 100644 --- a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php +++ b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php @@ -18,8 +18,6 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; if (!function_exists('get_webfinger_data')) { /** * Retrieve actor webfinger data from username and domain - * - * @return object|null */ function get_webfinger_data(string $username, string $domain): ?object { @@ -45,8 +43,7 @@ if (!function_exists('split_handle')) { /** * Splits handle into its parts (username, host and port) * - * @param string $handle - * @return bool|array + * @return array<string, string>|false */ function split_handle(string $handle) { @@ -107,7 +104,7 @@ if (!function_exists('accept_follow')) { ); $acceptRequest->sign($actor->public_key_id, $actor->private_key); $acceptRequest->post(); - } catch (Exception $exception) { + } catch (Exception) { $db->transRollback(); } @@ -163,8 +160,6 @@ if (!function_exists('extract_urls_from_message')) { if (!function_exists('create_preview_card_from_url')) { /** * Extract open graph metadata from given url and create preview card - * - * @return PreviewCard|null */ function create_preview_card_from_url(URI $url): ?PreviewCard { @@ -223,8 +218,6 @@ if (!function_exists('create_preview_card_from_url')) { if (!function_exists('get_or_create_preview_card_from_url')) { /** * Extract open graph metadata from given url and create preview card - * - * @return PreviewCard|null */ function get_or_create_preview_card_from_url(URI $url): ?PreviewCard { @@ -246,8 +239,6 @@ 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. - * - * @return Actor|null */ function get_or_create_actor_from_uri(string $actorUri): ?Actor { @@ -265,8 +256,6 @@ 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. - * - * @return Actor|null */ function get_or_create_actor(string $username, string $domain): ?Actor { @@ -292,8 +281,6 @@ if (!function_exists('create_actor_from_uri')) { /** * Creates actor record in database using * the info gathered from the actorUri parameter - * - * @return Actor|null */ function create_actor_from_uri(string $actorUri): ?Actor { @@ -352,8 +339,6 @@ if (!function_exists('get_current_domain')) { if (!function_exists('extract_text_from_html')) { /** * Extracts the text from html content - * - * @return string|null */ function extract_text_from_html(string $content): ?string { @@ -381,7 +366,7 @@ if (!function_exists('linkify')) { case 'https': $text = preg_replace_callback( '~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i', - function ($match) use ($protocol, &$links) { + function (array $match) use ($protocol, &$links) { if ($match[1]) { $protocol = $match[1]; } @@ -452,7 +437,7 @@ if (!function_exists('linkify')) { ]), ) . '>'; - } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $httpException) { + } catch (\CodeIgniter\HTTP\Exceptions\HTTPException) { // Couldn't retrieve actor, do not wrap the text in link return '<' . array_push($links, $match[0]) . @@ -485,7 +470,7 @@ if (!function_exists('linkify')) { '~' . preg_quote($protocol, '~') . '://([^\s<]+?)(?<![\.,:])~i', - function ($match) use ($protocol, &$links) { + function (array $match) use ($protocol, &$links) { return '<' . array_push( $links, diff --git a/app/Libraries/ActivityPub/HttpSignature.php b/app/Libraries/ActivityPub/HttpSignature.php index 30d301960d..f62e2fe690 100644 --- a/app/Libraries/ActivityPub/HttpSignature.php +++ b/app/Libraries/ActivityPub/HttpSignature.php @@ -41,7 +41,7 @@ class HttpSignature /** * @var IncomingRequest */ - protected $request; + protected ?IncomingRequest $request; public function __construct(IncomingRequest $request = null) { @@ -130,9 +130,9 @@ class HttpSignature /** * Split HTTP signature into its parts (keyId, headers and signature) * - * @return bool|mixed + * @return array<string, string>|false */ - private function splitSignature(string $signature) + private function splitSignature(string $signature): array|false { if (!preg_match(self::SIGNATURE_PATTERN, $signature, $matches)) { // Signature pattern failed @@ -150,7 +150,7 @@ class HttpSignature /** * Get plain text that has been originally signed * - * @param array $headers HTTP header keys + * @param string[] $headers HTTP header keys */ private function getPlainText(array $headers): string { diff --git a/app/Libraries/ActivityPub/Models/ActivityModel.php b/app/Libraries/ActivityPub/Models/ActivityModel.php index 388eddb52b..13d4114c38 100644 --- a/app/Libraries/ActivityPub/Models/ActivityModel.php +++ b/app/Libraries/ActivityPub/Models/ActivityModel.php @@ -10,6 +10,7 @@ namespace ActivityPub\Models; use ActivityPub\Entities\Activity; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\I18n\Time; use DateTimeInterface; use Michalsn\Uuid\UuidModel; @@ -59,7 +60,7 @@ class ActivityModel extends UuidModel protected $useTimestamps = true; protected $updatedField; - public function getActivityById($activityId) + public function getActivityById(string $activityId): ?Activity { $cacheName = config('ActivityPub')->cachePrefix . "activity#{$activityId}"; @@ -76,8 +77,6 @@ class ActivityModel extends UuidModel * Inserts a new activity record in the database * * @param Time $scheduledAt - * - * @return BaseResult|int|string|false */ public function newActivity( string $type, @@ -87,7 +86,7 @@ class ActivityModel extends UuidModel string $payload, DateTimeInterface $scheduledAt = null, ?string $status = null - ) { + ): BaseResult|int|string|false { return $this->insert( [ 'actor_id' => $actorId, @@ -102,7 +101,10 @@ class ActivityModel extends UuidModel ); } - public function getScheduledActivities() + /** + * @return Activity[] + */ + public function getScheduledActivities(): array { return $this->where('`scheduled_at` <= NOW()', null, false) ->where('status', 'queued') diff --git a/app/Libraries/ActivityPub/Models/ActorModel.php b/app/Libraries/ActivityPub/Models/ActorModel.php index 795d5c8ffa..93871e108b 100644 --- a/app/Libraries/ActivityPub/Models/ActorModel.php +++ b/app/Libraries/ActivityPub/Models/ActorModel.php @@ -58,7 +58,7 @@ class ActorModel extends Model */ protected $useTimestamps = true; - public function getActorById($id): Actor + public function getActorById(int $id): Actor { $cacheName = config('ActivityPub')->cachePrefix . "actor#{$id}"; if (!($found = cache($cacheName))) { @@ -98,7 +98,7 @@ class ActorModel extends Model return $found; } - public function getActorByUri($actorUri) + public function getActorByUri(string $actorUri): ?Actor { $hashedActorUri = md5($actorUri); $cacheName = @@ -112,7 +112,10 @@ class ActorModel extends Model return $found; } - public function getFollowers($actorId) + /** + * @return Actor[] + */ + public function getFollowers(int $actorId): array { $cacheName = config('ActivityPub')->cachePrefix . "actor#{$actorId}_followers"; @@ -137,7 +140,7 @@ class ActorModel extends Model */ public function isActorBlocked(string $actorUri): bool { - if ($actor = $this->getActorByUri($actorUri)) { + if (($actor = $this->getActorByUri($actorUri)) !== null) { return $actor->is_blocked; } @@ -161,7 +164,7 @@ class ActorModel extends Model return $found; } - public function blockActor($actorId): void + public function blockActor(int $actorId): void { $prefix = config('ActivityPub')->cachePrefix; cache()->delete($prefix . 'blocked_actors'); @@ -172,7 +175,7 @@ class ActorModel extends Model $this->update($actorId, ['is_blocked' => 1]); } - public function unblockActor($actorId): void + public function unblockActor(int $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 5f67279b07..2e2745a412 100644 --- a/app/Libraries/ActivityPub/Models/BlockedDomainModel.php +++ b/app/Libraries/ActivityPub/Models/BlockedDomainModel.php @@ -49,8 +49,10 @@ class BlockedDomainModel extends Model /** * Retrieves instance or podcast domain blocks depending on whether or not $podcastId param is set. + * + * @return BlockedDomain[] */ - public function getBlockedDomains() + public function getBlockedDomains(): array { $cacheName = config('ActivityPub')->cachePrefix . 'blocked_domains'; if (!($found = cache($cacheName))) { @@ -61,14 +63,14 @@ class BlockedDomainModel extends Model return $found; } - public function isDomainBlocked($domain) + public function isDomainBlocked(string $name): bool { - $hashedDomain = md5($domain); + $hashedDomainName = md5($name); $cacheName = config('ActivityPub')->cachePrefix . - "domain#{$hashedDomain}_isBlocked"; + "domain#{$hashedDomainName}_isBlocked"; if (!($found = cache($cacheName))) { - $found = (bool) $this->find($domain); + $found = (bool) $this->find($name); cache()->save($cacheName, $found, DECADE); } @@ -76,7 +78,7 @@ class BlockedDomainModel extends Model return $found; } - public function blockDomain($name) + public function blockDomain(string $name): int|bool { $hashedDomain = md5($name); $prefix = config('ActivityPub')->cachePrefix; @@ -104,10 +106,7 @@ class BlockedDomainModel extends Model return $result; } - /** - * @return bool|BaseResult - */ - public function unblockDomain($name) + public function unblockDomain(string $name): BaseResult|bool { $hashedDomain = md5($name); $prefix = config('ActivityPub')->cachePrefix; diff --git a/app/Libraries/ActivityPub/Models/FavouriteModel.php b/app/Libraries/ActivityPub/Models/FavouriteModel.php index b61fba3915..c6480a1352 100644 --- a/app/Libraries/ActivityPub/Models/FavouriteModel.php +++ b/app/Libraries/ActivityPub/Models/FavouriteModel.php @@ -109,9 +109,9 @@ class FavouriteModel extends UuidModel } public function removeFavourite( - $actor, - $note, - $registerActivity = true + Actor $actor, + Note $note, + bool $registerActivity = true ): void { $this->db->transStart(); diff --git a/app/Libraries/ActivityPub/Models/FollowModel.php b/app/Libraries/ActivityPub/Models/FollowModel.php index 4bd0031bf0..09ec65577f 100644 --- a/app/Libraries/ActivityPub/Models/FollowModel.php +++ b/app/Libraries/ActivityPub/Models/FollowModel.php @@ -103,7 +103,7 @@ class FollowModel extends Model } $this->db->transComplete(); - } catch (Exception $exception) { + } catch (Exception) { // follow already exists, do nothing } } @@ -117,7 +117,7 @@ class FollowModel extends Model public function removeFollower( Actor $actor, Actor $targetActor, - $registerActivity = true + bool $registerActivity = true ): void { $this->db->transStart(); diff --git a/app/Libraries/ActivityPub/Models/NoteModel.php b/app/Libraries/ActivityPub/Models/NoteModel.php index 2a5e0da617..01c0988482 100644 --- a/app/Libraries/ActivityPub/Models/NoteModel.php +++ b/app/Libraries/ActivityPub/Models/NoteModel.php @@ -21,6 +21,8 @@ use CodeIgniter\Database\BaseResult; use CodeIgniter\Events\Events; use CodeIgniter\HTTP\URI; use CodeIgniter\I18n\Time; +use CodeIgniter\Router\Exceptions\RouterException; +use InvalidArgumentException; use Michalsn\Uuid\UuidModel; class NoteModel extends UuidModel @@ -86,7 +88,7 @@ class NoteModel extends UuidModel */ protected $beforeInsert = ['setNoteId']; - public function getNoteById($noteId) + public function getNoteById(string $noteId): ?Note { $cacheName = config('ActivityPub')->cachePrefix . "note#{$noteId}"; if (!($found = cache($cacheName))) { @@ -98,7 +100,7 @@ class NoteModel extends UuidModel return $found; } - public function getNoteByUri($noteUri) + public function getNoteByUri(string $noteUri): ?Note { $hashedNoteUri = md5($noteUri); $cacheName = @@ -117,7 +119,7 @@ class NoteModel extends UuidModel * * @return Note[] */ - public function getActorPublishedNotes($actorId): array + public function getActorPublishedNotes(int $actorId): array { $cacheName = config('ActivityPub')->cachePrefix . @@ -179,8 +181,10 @@ class NoteModel extends UuidModel /** * Retrieves all published reblogs for a given note + * + * @return Note[] */ - public function getNoteReblogs($noteId) + public function getNoteReblogs(string $noteId): array { $cacheName = config('ActivityPub')->cachePrefix . "note#{$noteId}_reblogs"; @@ -200,10 +204,7 @@ class NoteModel extends UuidModel return $found; } - /** - * @return bool|Query - */ - public function addPreviewCard($noteId, $previewCardId) + public function addPreviewCard(string $noteId, int $previewCardId): Query|bool { return $this->db->table('activitypub_notes_preview_cards')->insert([ 'note_id' => $this->uuid->fromString($noteId)->getBytes(), @@ -220,7 +221,7 @@ class NoteModel extends UuidModel Note $note, bool $createPreviewCard = true, bool $registerActivity = true - ) { + ): string|false { helper('activitypub'); $this->db->transStart(); @@ -301,7 +302,7 @@ class NoteModel extends UuidModel return $newNoteId; } - public function editNote($updatedNote): bool + public function editNote(Note $updatedNote): bool { $this->db->transStart(); @@ -341,10 +342,8 @@ class NoteModel extends UuidModel /** * Removes a note from the database and decrements meta data - * - * @return BaseResult|bool */ - public function removeNote(Note $note, bool $registerActivity = true) + public function removeNote(Note $note, bool $registerActivity = true): BaseResult|bool { $this->db->transStart(); @@ -450,14 +449,11 @@ class NoteModel extends UuidModel return $result; } - /** - * @return string|bool - */ public function addReply( - $reply, - $createPreviewCard = true, - $registerActivity = true - ) { + Note $reply, + bool $createPreviewCard = true, + bool $registerActivity = true + ): string|false { if (!$reply->in_reply_to_id) { throw new Exception('Passed note is not a reply!'); } @@ -489,10 +485,7 @@ class NoteModel extends UuidModel return $noteId; } - /** - * @return BaseResult|int|string|false - */ - public function reblog(Actor $actor, Note $note, $registerActivity = true) + public function reblog(Actor $actor, Note $note, bool $registerActivity = true): string|false { $this->db->transStart(); @@ -503,7 +496,7 @@ class NoteModel extends UuidModel ]); // add reblog - $reblogId = $this->insert($reblog, true); + $reblogId = $this->insert($reblog); model('ActorModel') ->where('id', $actor->id) @@ -554,10 +547,7 @@ class NoteModel extends UuidModel return $reblogId; } - /** - * @return BaseResult|bool - */ - public function undoReblog(Note $reblogNote, bool $registerActivity = true) + public function undoReblog(Note $reblogNote, bool $registerActivity = true): BaseResult|bool { $this->db->transStart(); @@ -649,7 +639,7 @@ class NoteModel extends UuidModel return $result; } - public function toggleReblog($actor, $note): void + public function toggleReblog(Actor $actor, Note $note): void { if ( !($reblogNote = $this->where([ @@ -665,7 +655,11 @@ class NoteModel extends UuidModel } } - protected function setNoteId($data) + /** + * @param array<string, array<string|int, mixed>> $data + * @return array<string, array<string|int, mixed>> + */ + protected function setNoteId(array $data): array { $uuid4 = $this->uuid->{$this->uuidVersion}(); $data['data']['id'] = $uuid4->toString(); diff --git a/app/Libraries/ActivityPub/Models/PreviewCardModel.php b/app/Libraries/ActivityPub/Models/PreviewCardModel.php index 5580e7bb94..b6ae826837 100644 --- a/app/Libraries/ActivityPub/Models/PreviewCardModel.php +++ b/app/Libraries/ActivityPub/Models/PreviewCardModel.php @@ -51,7 +51,7 @@ class PreviewCardModel extends Model */ protected $useTimestamps = true; - public function getPreviewCardFromUrl($url) + public function getPreviewCardFromUrl(string $url): ?PreviewCard { $hashedPreviewCardUrl = md5($url); $cacheName = @@ -65,7 +65,7 @@ class PreviewCardModel extends Model return $found; } - public function getNotePreviewCard($noteId) + public function getNotePreviewCard(string $noteId): ?PreviewCard { $cacheName = config('ActivityPub')->cachePrefix . "note#{$noteId}_preview_card"; @@ -89,10 +89,7 @@ class PreviewCardModel extends Model return $found; } - /** - * @return bool|BaseResult - */ - public function deletePreviewCard($id, $url) + public function deletePreviewCard(int $id, string $url): BaseResult|bool { $hashedPreviewCardUrl = md5($url); cache()->delete( diff --git a/app/Libraries/ActivityPub/Objects/ActorObject.php b/app/Libraries/ActivityPub/Objects/ActorObject.php index 766f09460f..20cc9cb83d 100644 --- a/app/Libraries/ActivityPub/Objects/ActorObject.php +++ b/app/Libraries/ActivityPub/Objects/ActorObject.php @@ -14,7 +14,7 @@ use ActivityPub\Core\ObjectType; class ActorObject extends ObjectType { /** - * @var array|string + * @var string|string[] */ protected $context = [ 'https://www.w3.org/ns/activitystreams', @@ -62,12 +62,12 @@ class ActorObject extends ObjectType protected $url; /** - * @var array|null + * @var array<string, string>|null */ protected $image; /** - * @var array + * @var array<string, string> */ protected $icon = []; diff --git a/app/Libraries/ActivityPub/Objects/NoteObject.php b/app/Libraries/ActivityPub/Objects/NoteObject.php index 31d7556d54..2a43392770 100644 --- a/app/Libraries/ActivityPub/Objects/NoteObject.php +++ b/app/Libraries/ActivityPub/Objects/NoteObject.php @@ -38,10 +38,7 @@ class NoteObject extends ObjectType */ protected $replies; - /** - * @param Note $note - */ - public function __construct($note) + public function __construct(Note $note) { $this->id = $note->uri; diff --git a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php index 74e5f2d590..e407965a7e 100644 --- a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php +++ b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php @@ -42,15 +42,10 @@ class OrderedCollectionObject extends ObjectType protected $last; /** - * @var array|null - */ - protected $orderedItems; - - /** - * @param array $orderedItems + * @param ObjectType[] $orderedItems */ public function __construct( - ?array $orderedItems = null, + protected ?array $orderedItems = null, ?Pager $pager = null ) { $this->id = current_url(); @@ -65,7 +60,5 @@ class OrderedCollectionObject extends ObjectType $this->last = $pager->getPageURI($pager->getLastPage()); } } - - $this->orderedItems = $orderedItems; } } diff --git a/app/Libraries/ActivityPub/WebFinger.php b/app/Libraries/ActivityPub/WebFinger.php index 85edf401a7..09113f9289 100644 --- a/app/Libraries/ActivityPub/WebFinger.php +++ b/app/Libraries/ActivityPub/WebFinger.php @@ -38,29 +38,19 @@ class WebFinger protected $port; /** - * @var string - */ - protected $subject; - - /** - * @var array + * @var string[] */ protected $aliases = []; /** - * @var array + * @var array<array<string, string>> */ protected $links = []; - /** - * @param string $resource - */ - public function __construct($resource) + public function __construct(protected string $subject) { - $this->subject = $resource; - // Split resource into its parts (username, domain) - $parts = $this->splitResource($resource); + $parts = $this->splitResource($subject); if (!$parts) { throw new Exception('Wrong WebFinger resource pattern.'); } @@ -120,9 +110,9 @@ class WebFinger /** * Split resource into its parts (username, domain) * - * @return bool|mixed + * @return array<string, string>|false */ - private function splitResource(string $resource) + private function splitResource(string $resource): array|false { if (!preg_match(self::RESOURCE_PATTERN, $resource, $matches)) { // Resource pattern failed diff --git a/app/Libraries/Analytics/AnalyticsTrait.php b/app/Libraries/Analytics/AnalyticsTrait.php index 4f9a374c5b..0e05b9ed1a 100644 --- a/app/Libraries/Analytics/AnalyticsTrait.php +++ b/app/Libraries/Analytics/AnalyticsTrait.php @@ -10,6 +10,7 @@ namespace Analytics; use Config\Services; use Config\Database; + trait AnalyticsTrait { protected function registerPodcastWebpageHit(int $podcastId): void diff --git a/app/Libraries/Analytics/Config/Analytics.php b/app/Libraries/Analytics/Config/Analytics.php index 1ab066edd5..7b00ebb4f3 100644 --- a/app/Libraries/Analytics/Config/Analytics.php +++ b/app/Libraries/Analytics/Config/Analytics.php @@ -31,7 +31,7 @@ class Analytics extends BaseConfig * * @param string|string[] $audioFilePath */ - public function getAudioFileUrl($audioFilePath): string + public function getAudioFileUrl(string|array $audioFilePath): string { return base_url($audioFilePath); } diff --git a/app/Libraries/Analytics/Controllers/AnalyticsController.php b/app/Libraries/Analytics/Controllers/AnalyticsController.php index 139892b332..9f0ea4ace1 100644 --- a/app/Libraries/Analytics/Controllers/AnalyticsController.php +++ b/app/Libraries/Analytics/Controllers/AnalyticsController.php @@ -24,7 +24,7 @@ class AnalyticsController extends Controller */ protected $methodName; - public function _remap($method, ...$params) + public function _remap(string $method, string ...$params): mixed { if (!isset($params[1])) { throw PageNotFoundException::forPageNotFound(); @@ -39,11 +39,11 @@ class AnalyticsController extends Controller ); } - public function getData($podcastId, $episodeId): ResponseInterface + public function getData(int $podcastId, int $episodeId): ResponseInterface { $analytics_model = new $this->className(); $methodName = $this->methodName; - if ($episodeId) { + if ($episodeId !== 0) { return $this->response->setJSON( $analytics_model->$methodName($podcastId, $episodeId), ); diff --git a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php index a8dfd1a751..5c02415e5e 100644 --- a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php +++ b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php @@ -23,7 +23,7 @@ class EpisodeAnalyticsController extends Controller * class instantiation. These helpers will be available * to all other controllers that extend Analytics. * - * @var array + * @var string[] */ protected $helpers = ['analytics']; diff --git a/app/Libraries/Analytics/Controllers/EpisodeController.php b/app/Libraries/Analytics/Controllers/EpisodeController.php new file mode 100644 index 0000000000..ec531e16c0 --- /dev/null +++ b/app/Libraries/Analytics/Controllers/EpisodeController.php @@ -0,0 +1,200 @@ +<?php + +/** + * @copyright 2020 Podlibre + * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 + * @link https://castopod.org/ + */ + +namespace App\Controllers; + +use CodeIgniter\HTTP\ResponseInterface; +use Config\Services; +use Analytics\AnalyticsTrait; +use App\Entities\Episode; +use App\Entities\Podcast; +use App\Models\EpisodeModel; +use App\Models\PodcastModel; +use CodeIgniter\Exceptions\PageNotFoundException; +use SimpleXMLElement; + +class EpisodeController extends BaseController +{ + use AnalyticsTrait; + + /** + * @var Podcast + */ + protected $podcast; + + /** + * @var Episode + */ + protected $episode; + + public function _remap(string $method, string ...$params): mixed + { + if (count($params) < 2) { + throw PageNotFoundException::forPageNotFound(); + } + + if ( + ($this->podcast = (new PodcastModel())->getPodcastByName( + $params[0], + )) === null + ) { + throw PageNotFoundException::forPageNotFound(); + } + + if ( + ($this->episode = (new EpisodeModel())->getEpisodeBySlug( + $this->podcast->id, + $params[1], + )) !== null + ) { + unset($params[1]); + unset($params[0]); + return $this->$method(...$params); + } + + throw PageNotFoundException::forPageNotFound(); + } + + public function index(): string + { + // Prevent analytics hit when authenticated + if (!can_user_interact()) { + $this->registerPodcastWebpageHit($this->episode->podcast_id); + } + + $locale = service('request')->getLocale(); + $cacheName = + "page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_{$locale}" . + (can_user_interact() ? '_authenticated' : ''); + + if (!($cachedView = cache($cacheName))) { + $data = [ + 'podcast' => $this->podcast, + 'episode' => $this->episode, + ]; + + $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( + $this->podcast->id, + ); + + if (can_user_interact()) { + helper('form'); + return view('podcast/episode_authenticated', $data); + } + // The page cache is set to a decade so it is deleted manually upon podcast update + return view('podcast/episode', $data, [ + 'cache' => $secondsToNextUnpublishedEpisode + ? $secondsToNextUnpublishedEpisode + : DECADE, + 'cache_name' => $cacheName, + ]); + } + + return $cachedView; + } + + public function embeddablePlayer( + string $theme = 'light-transparent' + ): string { + header('Content-Security-Policy: frame-ancestors https://* http://*'); + + // Prevent analytics hit when authenticated + if (!can_user_interact()) { + $this->registerPodcastWebpageHit($this->episode->podcast_id); + } + + $session = Services::session(); + $session->start(); + if (isset($_SERVER['HTTP_REFERER'])) { + $session->set( + 'embeddable_player_domain', + parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), + ); + } + + $locale = service('request')->getLocale(); + + $cacheName = "page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_embeddable_player_{$theme}_{$locale}"; + + if (!($cachedView = cache($cacheName))) { + $theme = EpisodeModel::$themes[$theme]; + + $data = [ + 'podcast' => $this->podcast, + 'episode' => $this->episode, + 'theme' => $theme, + ]; + + $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( + $this->podcast->id, + ); + + // The page cache is set to a decade so it is deleted manually upon podcast update + return view('embeddable_player', $data, [ + 'cache' => $secondsToNextUnpublishedEpisode + ? $secondsToNextUnpublishedEpisode + : DECADE, + 'cache_name' => $cacheName, + ]); + } + + return $cachedView; + } + + public function oembedJSON(): ResponseInterface + { + return $this->response->setJSON([ + 'type' => 'rich', + 'version' => '1.0', + 'title' => $this->episode->title, + 'provider_name' => $this->podcast->title, + 'provider_url' => $this->podcast->link, + 'author_name' => $this->podcast->title, + 'author_url' => $this->podcast->link, + 'html' => + '<iframe src="' . + $this->episode->embeddable_player_url . + '" width="100%" height="200" frameborder="0" scrolling="no"></iframe>', + 'width' => 600, + 'height' => 200, + 'thumbnail_url' => $this->episode->image->large_url, + 'thumbnail_width' => config('Images')->largeSize, + 'thumbnail_height' => config('Images')->largeSize, + ]); + } + + public function oembedXML(): ResponseInterface + { + $oembed = new SimpleXMLElement( + "<?xml version='1.0' encoding='utf-8' standalone='yes'?><oembed></oembed>", + ); + + $oembed->addChild('type', 'rich'); + $oembed->addChild('version', '1.0'); + $oembed->addChild('title', $this->episode->title); + $oembed->addChild('provider_name', $this->podcast->title); + $oembed->addChild('provider_url', $this->podcast->link); + $oembed->addChild('author_name', $this->podcast->title); + $oembed->addChild('author_url', $this->podcast->link); + $oembed->addChild('thumbnail', $this->episode->image->large_url); + $oembed->addChild('thumbnail_width', config('Images')->largeSize); + $oembed->addChild('thumbnail_height', config('Images')->largeSize); + $oembed->addChild( + 'html', + htmlentities( + '<iframe src="' . + $this->episode->embeddable_player_url . + '" width="100%" height="200" frameborder="0" scrolling="no"></iframe>', + ), + ); + $oembed->addChild('width', '600'); + $oembed->addChild('height', '200'); + + return $this->response->setXML($oembed); + } +} diff --git a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php index 7d5549a026..5c9e424278 100644 --- a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php +++ b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php @@ -13,9 +13,9 @@ use CodeIgniter\Controller; class UnknownUserAgentsController extends Controller { - public function index($lastKnownId = 0): ResponseInterface + public function index(int $lastKnownId = 0): ResponseInterface { - $model = model('UnknownUserAgentsModel'); + $model = model('AnalyticsUnknownUserAgentsModel'); return $this->response->setJSON($model->getUserAgents($lastKnownId)); } diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php index 6744b9ac82..ae231232ce 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php @@ -37,7 +37,7 @@ class AnalyticsPodcastsByCountry extends Entity 'hits' => 'integer', ]; - public function getLabels() + public function getLabels(): string { return lang('Countries.' . $this->attributes['labels']); } diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php index dad55121ec..b1a56e6f75 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php @@ -42,7 +42,7 @@ class AnalyticsPodcastsByRegion extends Entity 'hits' => 'integer', ]; - public function getCountryCode() + public function getCountryCode(): string { return lang('Countries.' . $this->attributes['country_code']); } diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php index 2022f28391..82218943e4 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php @@ -44,7 +44,7 @@ class AnalyticsPodcastsByService extends Entity 'hits' => 'integer', ]; - public function getLabels() + public function getLabels(): string { return UserAgentsRSS::getName($this->attributes['labels']) ?? $this->attributes['labels']; diff --git a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php b/app/Libraries/Analytics/Entities/AnalyticsUnknownUserAgent.php similarity index 74% rename from app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php rename to app/Libraries/Analytics/Entities/AnalyticsUnknownUserAgent.php index b442849107..7cf4f45534 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php +++ b/app/Libraries/Analytics/Entities/AnalyticsUnknownUserAgent.php @@ -13,17 +13,24 @@ namespace Analytics\Entities; use CodeIgniter\Entity\Entity; /** + * @property int $id * @property int $useragent * @property int $hits * @property Time $created_at * @property Time $updated_at */ -class AnalyticsUnknownUseragents extends Entity +class AnalyticsUnknownUserAgent extends Entity { + /** + * @var string[] + */ + protected $dates = ['created_at', 'updated_at']; + /** * @var array<string, string> */ protected $casts = [ + 'id' => 'integer', 'useragent' => 'integer', 'hits' => 'integer', ]; diff --git a/app/Libraries/Analytics/Helpers/analytics_helper.php b/app/Libraries/Analytics/Helpers/analytics_helper.php index 364eab1ce5..ed2cec5f8a 100644 --- a/app/Libraries/Analytics/Helpers/analytics_helper.php +++ b/app/Libraries/Analytics/Helpers/analytics_helper.php @@ -1,5 +1,6 @@ <?php +use CodeIgniter\I18n\Time; use Config\Services; use Podlibre\Ipcat\IpDb; use GeoIp2\Database\Reader; @@ -18,7 +19,7 @@ if (!function_exists('base64_url_encode')) { /** * Encode Base64 for URLs */ - function base64_url_encode($input) + function base64_url_encode(string $input): string { return strtr(base64_encode($input), '+/=', '._-'); } @@ -28,7 +29,7 @@ if (!function_exists('base64_url_decode')) { /** * Decode Base64 from URL */ - function base64_url_decode($input) + function base64_url_decode(string $input): string { return base64_decode(strtr($input, '._-', '+/=')); } @@ -131,7 +132,7 @@ if (!function_exists('set_user_session_location')) { 'longitude' => round($city->location->longitude, 3), ]; // If things go wrong the show must go on and the user must be able to download the file - } catch (Exception $exception) { + } catch (Exception) { } $session->set('location', $location); } @@ -154,7 +155,7 @@ if (!function_exists('set_user_session_player')) { try { $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) { + } catch (Exception) { } if ($playerFound) { $session->set('player', $playerFound); @@ -176,7 +177,7 @@ if (!function_exists('set_user_session_player')) { [$userAgent], ); // If things go wrong the show must go on and the user must be able to download the file - } catch (Exception $exception) { + } catch (Exception) { } } } @@ -197,7 +198,7 @@ if (!function_exists('set_user_session_browser')) { try { $whichbrowser = new Parser(getallheaders()); $browserName = $whichbrowser->browser->name; - } catch (Exception $exception) { + } catch (Exception) { $browserName = '- Could not get browser name -'; } if ($browserName == null) { @@ -267,6 +268,8 @@ if (!function_exists('podcast_hit')) { * @param integer $episodeId The Episode ID * @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 integer $duration The episode duration in seconds + * @param int $publicationTime The episode's publication time as a UNIX timestamp * @param string $serviceName The name of the service that had fetched the RSS feed */ function podcast_hit( @@ -274,8 +277,8 @@ if (!function_exists('podcast_hit')) { int $episodeId, int $bytesThreshold, int $fileSize, - $duration, - $publicationDate, + int $duration, + int $publicationTime, string $serviceName ): void { $session = Services::session(); @@ -341,7 +344,7 @@ if (!function_exists('podcast_hit')) { $db = Database::connect(); $procedureName = $db->prefixTable('analytics_podcasts'); - $age = intdiv(time() - $publicationDate, 86400); + $age = intdiv(time() - $publicationTime, 86400); // We create a sha1 hash for this IP_Address+User_Agent+Podcast_ID (used to count unique listeners): $listenerHashId = diff --git a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php index c76772f652..1261e35529 100644 --- a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php @@ -10,24 +10,20 @@ namespace Analytics\Models; -use Analytics\Entities\AnalyticsUnknownUseragents; +use Analytics\Entities\AnalyticsUnknownUserAgent; use CodeIgniter\Model; -class AnalyticsUnknownUseragentsModel extends Model +class AnalyticsUnknownUserAgentModel extends Model { /** * @var string */ protected $table = 'analytics_unknown_useragents'; - /** - * @var string - */ - protected $primaryKey = 'id'; /** * @var string */ - protected $returnType = AnalyticsUnknownUseragents::class; + protected $returnType = AnalyticsUnknownUserAgent::class; /** * @var bool */ @@ -37,4 +33,12 @@ class AnalyticsUnknownUseragentsModel extends Model * @var bool */ protected $useTimestamps = false; + + /** + * @return mixed[] + */ + public function getUserAgents(int $lastKnownId = 0): array + { + return $this->where('id >', $lastKnownId)->findAll(); + } } diff --git a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php b/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php deleted file mode 100644 index b0173a5840..0000000000 --- a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * Class UnknownUserAgentsModel - * Model for analytics_unknown_useragents table in database - * @copyright 2020 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -namespace Analytics\Models; - -use CodeIgniter\Model; - -class UnknownUserAgentsModel extends Model -{ - /** - * @var string - */ - protected $table = 'analytics_unknown_useragents'; - - 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 e520b3987b..68d76bf072 100644 --- a/app/Libraries/Breadcrumb.php +++ b/app/Libraries/Breadcrumb.php @@ -15,11 +15,12 @@ class Breadcrumb /** * List of breadcrumb links. * - * @var array * $links = [ - * 'text' => (string) the anchor text, - * 'href' => (string) the anchor href, + * 'text' => 'Example Link', + * 'href' => 'https://example.com/', * ] + * + * @var array<array<string, string>> */ protected $links = []; @@ -57,6 +58,8 @@ class Breadcrumb * replaceParams($newParams); * * The breadcrumb is now `Home / podcasts / foo / episodes / bar` + * + * @param string[] $newParams */ public function replaceParams(array $newParams): void { @@ -71,7 +74,7 @@ class Breadcrumb /** * Renders the breadcrumb object as an accessible html breadcrumb nav */ - public function render($class = null): string + public function render(string $class = null): string { $listItems = ''; $keys = array_keys($this->links); diff --git a/app/Libraries/Negotiate.php b/app/Libraries/Negotiate.php index 22827dc5ee..3c8bd8932f 100644 --- a/app/Libraries/Negotiate.php +++ b/app/Libraries/Negotiate.php @@ -6,6 +6,9 @@ use CodeIgniter\HTTP\Negotiate as CodeIgniterHTTPNegotiate; class Negotiate extends CodeIgniterHTTPNegotiate { + /** + * @param mixed[] $acceptable + */ public function callMatch( array $acceptable, string $supported, diff --git a/app/Libraries/Router.php b/app/Libraries/Router.php index e9ac8b49f7..dc41fa9b2c 100644 --- a/app/Libraries/Router.php +++ b/app/Libraries/Router.php @@ -52,7 +52,7 @@ class Router extends CodeIgniterRouter $matchedKey = $key; // Are we dealing with a locale? - if (strpos($key, '{locale}') !== false) { + if (str_contains($key, '{locale}')) { $localeSegment = array_search( '{locale}', preg_split( @@ -167,9 +167,9 @@ class Router extends CodeIgniterRouter // Support resource route when function with subdirectory // ex: $routes->resource('Admin/Admins'); if ( - strpos($val, '$') !== false && - strpos($key, '(') !== false && - strpos($key, '/') !== false + str_contains($val, '$') && + str_contains($key, '(') && + str_contains($key, '/') ) { $replacekey = str_replace('/(.*)', '', $key); $val = preg_replace('#^' . $key . '$#u', $val, $uri); @@ -179,11 +179,11 @@ class Router extends CodeIgniterRouter $val, ); } elseif ( - strpos($val, '$') !== false && - strpos($key, '(') !== false + str_contains($val, '$') && + str_contains($key, '(') ) { $val = preg_replace('#^' . $key . '$#u', $val, $uri); - } elseif (strpos($val, '/') !== false) { + } elseif (str_contains($val, '/')) { [$controller, $method] = explode('::', $val); // Only replace slashes in the controller, not in the method. diff --git a/app/Models/ActorModel.php b/app/Models/ActorModel.php index 37a70b04be..b58b095578 100644 --- a/app/Models/ActorModel.php +++ b/app/Models/ActorModel.php @@ -13,5 +13,8 @@ use App\Entities\Actor; class ActorModel extends ActivityPubActorModel { + /** + * @var string + */ protected $returnType = Actor::class; } diff --git a/app/Models/CategoryModel.php b/app/Models/CategoryModel.php index 0bf5043626..d827fcd5f0 100644 --- a/app/Models/CategoryModel.php +++ b/app/Models/CategoryModel.php @@ -9,6 +9,7 @@ namespace App\Models; use App\Entities\Category; +use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Model; class CategoryModel extends Model @@ -46,12 +47,15 @@ class CategoryModel extends Model */ protected $useTimestamps = false; - public function getCategoryById($id): ?Category + public function getCategoryById(int $id): ?Category { return $this->find($id); } - public function getCategoryOptions() + /** + * @return array<int, string> + */ + public function getCategoryOptions(): array { $locale = service('request')->getLocale(); $cacheName = "category_options_{$locale}"; @@ -61,7 +65,7 @@ class CategoryModel extends Model $options = array_reduce( $categories, - function ($result, $category) { + function (array $result, Category $category): array { $result[$category->id] = lang( 'Podcast.category_options.' . $category->code, ); @@ -79,9 +83,11 @@ class CategoryModel extends Model /** * Sets categories for a given podcast * - * @return int|bool Number of rows inserted or FALSE on failure + * @param int[] $categories + * + * @return int|false Number of rows inserted or FALSE on failure */ - public function setPodcastCategories(int $podcastId, ?array $categories) + public function setPodcastCategories(int $podcastId, array $categories): int|false { cache()->delete("podcast#{$podcastId}_categories"); @@ -98,7 +104,7 @@ class CategoryModel extends Model // prepare data for `podcasts_categories` table $data = array_reduce( $categories, - function ($result, $categoryId) use ($podcastId) { + function (array $result, int $categoryId) use ($podcastId): array { $result[] = [ 'podcast_id' => $podcastId, 'category_id' => $categoryId, diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 7bf6614766..c39e741719 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -149,7 +149,7 @@ class EpisodeModel extends Model /** * @param int|string $podcastId may be the id or podcast name */ - public function getEpisodeBySlug($podcastId, string $episodeSlug): ?Episode + public function getEpisodeBySlug(int|string $podcastId, string $episodeSlug): ?Episode { $cacheName = "podcast#{$podcastId}_episode-{$episodeSlug}"; if (!($found = cache($cacheName))) { @@ -175,7 +175,7 @@ class EpisodeModel extends Model return $found; } - public function getEpisodeById($episodeId) + public function getEpisodeById(int $episodeId): ?Episode { // TODO: episode id should be a composite key. The cache should include podcast_id. $cacheName = "podcast_episode#{$episodeId}"; @@ -192,7 +192,7 @@ class EpisodeModel extends Model return $found; } - public function getPublishedEpisodeById($podcastId, $episodeId) + public function getPublishedEpisodeById(int $podcastId, int $episodeId): ?Episode { $cacheName = "podcast#{$podcastId}_episode#{$episodeId}_published"; if (!($found = cache($cacheName))) { @@ -278,7 +278,7 @@ class EpisodeModel extends Model * * @return int|bool seconds */ - public function getSecondsToNextUnpublishedEpisode(int $podcastId) + public function getSecondsToNextUnpublishedEpisode(int $podcastId): int|bool { $result = $this->select( 'TIMESTAMPDIFF(SECOND, NOW(), `published_at`) as timestamp_diff', @@ -297,9 +297,11 @@ class EpisodeModel extends Model } /** + * @param mixed[] $data + * * @return array<string, array<string|int, mixed>> */ - public function clearCache($data): array + public function clearCache(array $data): array { $episode = (new EpisodeModel())->find( is_array($data['id']) ? $data['id'][0] : $data['id'], @@ -348,6 +350,8 @@ class EpisodeModel extends Model } /** + * @param mixed[] $data + * * @return array<string, array<string|int, mixed>> */ protected function writeEnclosureMetadata(array $data): array diff --git a/app/Models/EpisodePersonModel.php b/app/Models/EpisodePersonModel.php deleted file mode 100644 index bec23995de..0000000000 --- a/app/Models/EpisodePersonModel.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php - -/** - * @copyright 2021 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -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', - 'episode_id', - 'person_id', - 'person_group', - 'person_role', - ]; - - /** - * @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', - ]; - - /** - * @var string[] - */ - protected $afterInsert = ['clearCache']; - /** - * @var string[] - */ - protected $beforeDelete = ['clearCache']; - - public function getEpisodePersons($podcastId, $episodeId) - { - $cacheName = "podcast#{$podcastId}_episode#{$episodeId}_persons"; - if (!($found = cache($cacheName))) { - $found = $this->select('episodes_persons.*') - ->where('episode_id', $episodeId) - ->join('persons', 'person_id=persons.id') - ->orderby('full_name') - ->findAll(); - - cache()->save($cacheName, $found, DECADE); - } - return $found; - } - - /** - * Add persons to episode - * - * @return bool|int Number of rows inserted or FALSE on failure - */ - public function addEpisodePersons( - int $podcastId, - int $episodeId, - array $persons, - array $groups_roles - ) { - if (!empty($persons)) { - $this->clearCache([ - 'episode_id' => $episodeId, - ]); - - $data = []; - foreach ($persons as $person) { - if ($groups_roles !== []) { - foreach ($groups_roles as $group_role) { - $group_role = explode(',', $group_role); - $data[] = [ - 'podcast_id' => $podcastId, - 'episode_id' => $episodeId, - 'person_id' => $person, - 'person_group' => $group_role[0], - 'person_role' => $group_role[1], - ]; - } - } else { - $data[] = [ - 'podcast_id' => $podcastId, - 'episode_id' => $episodeId, - 'person_id' => $person, - ]; - } - } - return $this->insertBatch($data); - } - return 0; - } - - /** - * @return bool|BaseResult - */ - public function removeEpisodePersons( - $podcastId, - $episodeId, - $episodePersonId - ) { - return $this->delete([ - 'podcast_id' => $podcastId, - 'episode_id' => $episodeId, - 'id' => $episodePersonId, - ]); - } - - /** - * @return array<string, array<string|int, mixed>> - */ - protected function clearCache(array $data): array - { - if (isset($data['episode_id'])) { - $episodeId = $data['episode_id']; - } else { - $person = (new EpisodePersonModel())->find( - is_array($data['id']) ? $data['id']['id'] : $data['id'], - ); - $episodeId = $person->episode_id; - } - - (new EpisodeModel())->clearCache(['id' => $episodeId]); - - return $data; - } -} diff --git a/app/Models/LanguageModel.php b/app/Models/LanguageModel.php index 09c52dfb92..b488ba981e 100644 --- a/app/Models/LanguageModel.php +++ b/app/Models/LanguageModel.php @@ -9,6 +9,7 @@ namespace App\Models; use App\Entities\Language; +use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Model; class LanguageModel extends Model @@ -41,14 +42,17 @@ class LanguageModel extends Model */ protected $useTimestamps = false; - public function getLanguageOptions() + /** + * @return array<string, string> + */ + public function getLanguageOptions(): array { if (!($options = cache('language_options'))) { $languages = $this->findAll(); $options = array_reduce( $languages, - function ($result, $language) { + function (array $result, Language $language): array { $result[$language->code] = $language->native_name; return $result; }, diff --git a/app/Models/NoteModel.php b/app/Models/NoteModel.php index 819df9a51b..4dbe62e300 100644 --- a/app/Models/NoteModel.php +++ b/app/Models/NoteModel.php @@ -8,12 +8,19 @@ namespace App\Models; +use App\Entities\Note; use ActivityPub\Models\NoteModel as ActivityPubNoteModel; class NoteModel extends ActivityPubNoteModel { - protected $returnType = \App\Entities\Note::class; + /** + * @var string + */ + protected $returnType = Note::class; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'uri', @@ -33,9 +40,9 @@ class NoteModel extends ActivityPubNoteModel /** * Retrieves all published notes for a given episode ordered by publication date * - * @return \App\Entities\Note[] + * @return Note[] */ - public function getEpisodeNotes($episodeId) + public function getEpisodeNotes(int $episodeId): array { return $this->where([ 'episode_id' => $episodeId, diff --git a/app/Models/PageModel.php b/app/Models/PageModel.php index f52eaf9438..c11f6dfc6f 100644 --- a/app/Models/PageModel.php +++ b/app/Models/PageModel.php @@ -69,6 +69,8 @@ class PageModel extends Model protected $beforeDelete = ['clearCache']; /** + * @param mixed[] $data + * * @return array<string, array<string|int, mixed>> */ protected function clearCache(array $data): array diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php index be18d6f5bd..ae861561d7 100644 --- a/app/Models/PersonModel.php +++ b/app/Models/PersonModel.php @@ -10,6 +10,7 @@ namespace App\Models; use App\Entities\Image; use App\Entities\Person; +use CodeIgniter\Database\BaseResult; use CodeIgniter\Model; class PersonModel extends Model @@ -80,7 +81,7 @@ class PersonModel extends Model */ protected $beforeDelete = ['clearCache']; - public function getPersonById($personId) + public function getPersonById(int $personId): ?Person { $cacheName = "person#{$personId}"; if (!($found = cache($cacheName))) { @@ -92,13 +93,16 @@ class PersonModel extends Model return $found; } - public function getPerson($fullName) + public function getPerson(string $fullName): ?Person { return $this->where('full_name', $fullName)->first(); } - public function createPerson($fullName, $informationUrl, $image) - { + public function addPerson( + string $fullName, + ?string $informationUrl, + string $image + ): int|bool { $person = new Person([ 'full_name' => $fullName, 'unique_name' => slugify($fullName), @@ -107,10 +111,200 @@ class PersonModel extends Model 'created_by' => user_id(), 'updated_by' => user_id(), ]); + return $this->insert($person); } - public function getPersonOptions() + /** + * @return Person[] + */ + public function getEpisodePersons(int $podcastId, int $episodeId): array + { + $cacheName = "podcast#{$podcastId}_episode#{$episodeId}_persons"; + if (!($found = cache($cacheName))) { + $found = $this->db + ->table('episodes_persons') + ->select('episodes_persons.*') + ->where('episode_id', $episodeId) + ->join('persons', 'person_id=persons.id') + ->orderby('full_name') + ->findAll(); + + cache()->save($cacheName, $found, DECADE); + } + + return $found; + } + + /** + * @return Person[] + */ + public function getPodcastPersons(int $podcastId): array + { + $cacheName = "podcast#{$podcastId}_persons"; + if (!($found = cache($cacheName))) { + $found = $this->db + ->table('podcasts_persons') + ->select('podcasts_persons.*') + ->where('podcast_id', $podcastId) + ->join('persons', 'person_id=persons.id') + ->orderby('full_name') + ->findAll(); + + cache()->save($cacheName, $found, DECADE); + } + + return $found; + } + + public function addEpisodePerson( + int $podcastId, + int $episodeId, + int $personId, + string $group, + string $role + ): int|bool { + return $this->db->table('episodes_persons')->insert([ + 'podcast_id' => $podcastId, + 'episode_id' => $episodeId, + 'person_id' => $personId, + 'person_group' => $group, + 'person_role' => $role, + ]); + } + + public function addPodcastPerson( + int $podcastId, + int $personId, + string $group, + string $role + ): int|bool { + return $this->db->table('podcasts_persons')->insert([ + 'podcast_id' => $podcastId, + 'person_id' => $personId, + 'person_group' => $group, + 'person_role' => $role, + ]); + } + + /** + * Add persons to podcast + * + * @param array<string> $persons + * @param array<string, string> $groupsRoles + * + * @return bool|int Number of rows inserted or FALSE on failure + */ + public function addPodcastPersons( + int $podcastId, + array $persons = [], + array $groupsRoles = [] + ): int|bool { + if ($persons === []) { + return 0; + } + + $this->clearCache(['podcast_id' => $podcastId]); + $data = []; + foreach ($persons as $person) { + if ($groupsRoles === []) { + $data[] = [ + 'podcast_id' => $podcastId, + 'person_id' => $person, + ]; + } + + foreach ($groupsRoles as $group_role) { + $group_role = explode(',', $group_role); + $data[] = [ + 'podcast_id' => $podcastId, + 'person_id' => $person, + 'person_group' => $group_role[0], + 'person_role' => $group_role[1], + ]; + } + } + + return $this->insertBatch($data); + } + + /** + * Add persons to episode + * + * @return BaseResult|bool Number of rows inserted or FALSE on failure + */ + public function removePodcastPersons(int $podcastId, int $personId): BaseResult|bool + { + return $this->delete([ + 'id' => $personId, + 'podcast_id' => $podcastId, + ]); + } + + /** + * Add persons to episode + * + * @param int[] $personIds + * @param string[] $groups_roles + * + * @return bool|int Number of rows inserted or FALSE on failure + */ + public function addEpisodePersons( + int $podcastId, + int $episodeId, + array $personIds, + array $groups_roles + ): bool|int { + if (!empty($personIds)) { + $this->clearCache([ + 'episode_id' => $episodeId, + ]); + + $data = []; + foreach ($personIds as $personId) { + if ($groups_roles !== []) { + foreach ($groups_roles as $group_role) { + $group_role = explode(',', $group_role); + $data[] = [ + 'podcast_id' => $podcastId, + 'episode_id' => $episodeId, + 'person_id' => $personId, + 'person_group' => $group_role[0], + 'person_role' => $group_role[1], + ]; + } + } else { + $data[] = [ + 'podcast_id' => $podcastId, + 'episode_id' => $episodeId, + 'person_id' => $personId, + ]; + } + } + return $this->insertBatch($data); + } + return 0; + } + + /** + * @return BaseResult|bool + */ + public function removeEpisodePersons( + int $podcastId, + int $episodeId, + int $personId + ): BaseResult|bool { + return $this->delete([ + 'podcast_id' => $podcastId, + 'episode_id' => $episodeId, + 'id' => $personId, + ]); + } + + /** + * @return array<string, string> + */ + public function getPersonOptions(): array { $options = []; @@ -131,7 +325,10 @@ class PersonModel extends Model return $options; } - public function getTaxonomyOptions() + /** + * @return array<string, string> + */ + public function getTaxonomyOptions(): array { $options = []; $locale = service('request')->getLocale(); @@ -156,6 +353,8 @@ class PersonModel extends Model } /** + * @param mixed[] $data + * * @return array<string, array<string|int, mixed>> */ protected function clearCache(array $data): array diff --git a/app/Models/PlatformModel.php b/app/Models/PlatformModel.php index 9ce8e241dc..aba70b0d6c 100644 --- a/app/Models/PlatformModel.php +++ b/app/Models/PlatformModel.php @@ -12,6 +12,7 @@ namespace App\Models; use App\Entities\Platform; +use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Model; class PlatformModel extends Model @@ -50,7 +51,10 @@ class PlatformModel extends Model */ protected $useTimestamps = false; - public function getPlatforms() + /** + * @return Platform[] + */ + public function getPlatforms(): array { if (!($found = cache('platforms'))) { $baseUrl = rtrim(config('app')->baseURL, '/'); @@ -62,7 +66,7 @@ class PlatformModel extends Model return $found; } - public function getPlatform($slug) + public function getPlatform(string $slug): ?Platform { $cacheName = "platform-{$slug}"; if (!($found = cache($cacheName))) { @@ -73,12 +77,12 @@ class PlatformModel extends Model } public function createPlatform( - $slug, - $type, - $label, - $homeUrl, - $submitUrl = null - ) { + string $slug, + string $type, + string $label, + string $homeUrl, + string $submitUrl = null + ): int|false { $data = [ 'slug' => $slug, 'type' => $type, @@ -86,10 +90,14 @@ class PlatformModel extends Model 'home_url' => $homeUrl, 'submit_url' => $submitUrl, ]; + return $this->insert($data, false); } - public function getPlatformsWithLinks($podcastId, $platformType) + /** + * @return Platform[] + */ + public function getPlatformsWithLinks(int $podcastId, string $platformType): array { if ( !($found = cache( @@ -117,7 +125,10 @@ class PlatformModel extends Model return $found; } - public function getPodcastPlatforms($podcastId, $platformType) + /** + * @return Platform[] + */ + public function getPodcastPlatforms(int $podcastId, string $platformType): array { $cacheName = "podcast#{$podcastId}_platforms_{$platformType}"; if (!($found = cache($cacheName))) { @@ -139,13 +150,13 @@ class PlatformModel extends Model } /** - * @return int|bool + * @param mixed[] $podcastsPlatformsData */ public function savePodcastPlatforms( - $podcastId, - $platformType, - $podcastsPlatformsData - ) { + int $podcastId, + string $platformType, + array $podcastsPlatformsData + ): int|false { $this->clearCache($podcastId); $podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms'); @@ -167,9 +178,9 @@ class PlatformModel extends Model } /** - * @return int|bool + * @param mixed[] $podcastsPlatformsData */ - public function createPodcastPlatforms($podcastId, $podcastsPlatformsData) + public function createPodcastPlatforms(int $podcastId, array $podcastsPlatformsData): int|false { $this->clearCache($podcastId); @@ -179,10 +190,7 @@ class PlatformModel extends Model ->insertBatch($podcastsPlatformsData); } - /** - * @return bool|string - */ - public function removePodcastPlatform($podcastId, $platformSlug) + public function removePodcastPlatform(int $podcastId, string $platformSlug): bool|string { $this->clearCache($podcastId); @@ -192,7 +200,7 @@ class PlatformModel extends Model ]); } - public function clearCache($podcastId): void + public function clearCache(int $podcastId): void { cache()->deleteMatching("podcast#{$podcastId}_platforms_*"); diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index 7eaf25ec40..daf491beb4 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -8,13 +8,21 @@ namespace App\Models; +use App\Entities\Podcast; +use CodeIgniter\Database\Query; use CodeIgniter\HTTP\URI; use CodeIgniter\Model; use phpseclib\Crypt\RSA; class PodcastModel extends Model { + /** + * @var string + */ protected $table = 'podcasts'; + /** + * @var string + */ protected $primaryKey = 'id'; /** @@ -55,11 +63,23 @@ class PodcastModel extends Model 'updated_by', ]; - protected $returnType = \App\Entities\Podcast::class; + /** + * @var string + */ + protected $returnType = Podcast::class; + /** + * @var bool + */ protected $useSoftDeletes = true; + /** + * @var bool + */ protected $useTimestamps = true; + /** + * @var array<string, string> + */ protected $validationRules = [ 'title' => 'required', 'name' => @@ -73,17 +93,35 @@ class PodcastModel extends Model 'created_by' => 'required', 'updated_by' => 'required', ]; + /** + * @var mixed[] + */ protected $validationMessages = []; + /** + * @var string[] + */ protected $beforeInsert = ['createPodcastActor']; + /** + * @var string[] + */ protected $afterInsert = ['setActorAvatar']; + /** + * @var string[] + */ protected $afterUpdate = ['updatePodcastActor']; // clear cache before update if by any chance, the podcast name changes, so will the podcast link + /** + * @var string[] + */ protected $beforeUpdate = ['clearCache']; + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; - public function getPodcastByName($podcastName) + public function getPodcastByName(string $podcastName): ?Podcast { $cacheName = "podcast-{$podcastName}"; if (!($found = cache($cacheName))) { @@ -94,7 +132,7 @@ class PodcastModel extends Model return $found; } - public function getPodcastById($podcastId) + public function getPodcastById(int $podcastId): ?Podcast { $cacheName = "podcast#{$podcastId}"; if (!($found = cache($cacheName))) { @@ -106,7 +144,7 @@ class PodcastModel extends Model return $found; } - public function getPodcastByActorId($actorId) + public function getPodcastByActorId(int $actorId): ?Podcast { $cacheName = "podcast_actor#{$actorId}"; if (!($found = cache($cacheName))) { @@ -121,11 +159,9 @@ class PodcastModel extends Model /** * Gets all the podcasts a given user is contributing to * - * @param int $userId - * - * @return \App\Entities\Podcast[] podcasts + * @return Podcast[] podcasts */ - public function getUserPodcasts($userId) + public function getUserPodcasts(int $userId): array { $cacheName = "user{$userId}_podcasts"; if (!($found = cache($cacheName))) { @@ -143,33 +179,33 @@ class PodcastModel extends Model return $found; } - public function addPodcastContributor($userId, $podcastId, $groupId) + public function addPodcastContributor(int $userId, int $podcastId, int $groupId): Query|bool { cache()->delete("podcast#{$podcastId}_contributors"); $data = [ - 'user_id' => (int) $userId, - 'podcast_id' => (int) $podcastId, - 'group_id' => (int) $groupId, + 'user_id' => $userId, + 'podcast_id' => $podcastId, + 'group_id' => $groupId, ]; return $this->db->table('podcasts_users')->insert($data); } - public function updatePodcastContributor($userId, $podcastId, $groupId) + public function updatePodcastContributor(int $userId, int $podcastId, int $groupId): bool { cache()->delete("podcast#{$podcastId}_contributors"); return $this->db ->table('podcasts_users') ->where([ - 'user_id' => (int) $userId, - 'podcast_id' => (int) $podcastId, + 'user_id' => $userId, + 'podcast_id' => $podcastId, ]) ->update(['group_id' => $groupId]); } - public function removePodcastContributor($userId, $podcastId) + public function removePodcastContributor(int $userId, int $podcastId): string|bool { cache()->delete("podcast#{$podcastId}_contributors"); @@ -182,7 +218,7 @@ class PodcastModel extends Model ->delete(); } - public function getContributorGroupId($userId, $podcastId) + public function getContributorGroupId(int $userId, int $podcastId): int|false { if (!is_numeric($podcastId)) { // identifier is the podcast name, request must be a join @@ -208,11 +244,14 @@ class PodcastModel extends Model ->getResultObject(); } - return (int) count($user_podcast) > 0 + return count($user_podcast) > 0 ? $user_podcast[0]->group_id : false; } + /** + * @return array<string, string>[] + */ public function getYears(int $podcastId): array { $cacheName = "podcast#{$podcastId}_years"; @@ -249,6 +288,9 @@ class PodcastModel extends Model return $found; } + /** + * @return array<string, string>[] + */ public function getSeasons(int $podcastId): array { $cacheName = "podcast#{$podcastId}_seasons"; @@ -286,11 +328,9 @@ class PodcastModel extends Model /** * Returns the default query for displaying the episode list on the podcast page * - * @param int $podcastId - * - * @return array|null + * @return array<string, mixed>|null */ - public function getDefaultQuery(int $podcastId) + public function getDefaultQuery(int $podcastId): ?array { $cacheName = "podcast#{$podcastId}_defaultQuery"; if (!($defaultQuery = cache($cacheName))) { @@ -301,12 +341,7 @@ class PodcastModel extends Model $defaultQuery = ['type' => 'season', 'data' => end($seasons)]; } else { $years = $this->getYears($podcastId); - if (!empty($years)) { - // get most recent year - $defaultQuery = ['type' => 'year', 'data' => $years[0]]; - } else { - $defaultQuery = null; - } + $defaultQuery = $years === [] ? null : ['type' => 'year', 'data' => $years[0]]; } cache()->save($cacheName, $defaultQuery, DECADE); @@ -318,9 +353,11 @@ class PodcastModel extends Model * Creates an actor linked to the podcast * (Triggered before insert) * - * @param array $data + * @param mixed[] $data + * + * @return mixed[] */ - protected function createPodcastActor(array $data) + protected function createPodcastActor(array $data): array { $rsa = new RSA(); $rsa->setHash('sha256'); @@ -356,7 +393,12 @@ class PodcastModel extends Model return $data; } - protected function setActorAvatar($data) + /** + * @param mixed[] $data + * + * @return mixed[] + */ + protected function setActorAvatar(array $data): array { $podcast = (new PodcastModel())->getPodcastById( is_array($data['id']) ? $data['id'][0] : $data['id'], @@ -372,7 +414,12 @@ class PodcastModel extends Model return $data; } - protected function updatePodcastActor(array $data) + /** + * @param mixed[] $data + * + * @return mixed[] + */ + protected function updatePodcastActor(array $data): array { $podcast = (new PodcastModel())->getPodcastById( is_array($data['id']) ? $data['id'][0] : $data['id'], @@ -394,7 +441,12 @@ class PodcastModel extends Model return $data; } - public function clearCache(array $data) + /** + * @param mixed[] $data + * + * @return mixed[] + */ + protected function clearCache(array $data): array { $podcast = (new PodcastModel())->getPodcastById( is_array($data['id']) ? $data['id'][0] : $data['id'], diff --git a/app/Models/PodcastPersonModel.php b/app/Models/PodcastPersonModel.php deleted file mode 100644 index 94904b9026..0000000000 --- a/app/Models/PodcastPersonModel.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php - -/** - * @copyright 2021 Podlibre - * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 - * @link https://castopod.org/ - */ - -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', - 'person_id', - 'person_group', - 'person_role', - ]; - - /** - * @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', - ]; - - /** - * @var string[] - */ - protected $afterInsert = ['clearCache']; - - /** - * @var string[] - */ - protected $beforeDelete = ['clearCache']; - - /** - * @return PodcastPerson[] - */ - public function getPodcastPersons(int $podcastId): array - { - $cacheName = "podcast#{$podcastId}_persons"; - if (!($found = cache($cacheName))) { - $found = $this->select('podcasts_persons.*') - ->where('podcast_id', $podcastId) - ->join('persons', 'person_id=persons.id') - ->orderby('full_name') - ->findAll(); - - cache()->save($cacheName, $found, DECADE); - } - - return $found; - } - - /** - * Add persons to podcast - * - * @param array<string> $persons - * @param array<string, string> $groupsRoles - * - * @return bool|int Number of rows inserted or FALSE on failure - */ - public function addPodcastPersons( - int $podcastId, - array $persons = [], - array $groupsRoles = [] - ) { - if ($persons === []) { - return 0; - } - - $this->clearCache(['podcast_id' => $podcastId]); - $data = []; - foreach ($persons as $person) { - if ($groupsRoles === []) { - $data[] = [ - 'podcast_id' => $podcastId, - 'person_id' => $person, - ]; - } - - foreach ($groupsRoles as $group_role) { - $group_role = explode(',', $group_role); - $data[] = [ - 'podcast_id' => $podcastId, - 'person_id' => $person, - 'person_group' => $group_role[0], - 'person_role' => $group_role[1], - ]; - } - } - - return $this->insertBatch($data); - } - - /** - * @return bool|BaseResult - */ - public function removePodcastPersons($podcastId, $podcastPersonId) - { - return $this->delete([ - 'podcast_id' => $podcastId, - 'id' => $podcastPersonId, - ]); - } - - /** - * @return array<string, array<string|int, mixed>> - */ - protected function clearCache(array $data): array - { - if (isset($data['podcast_id'])) { - $podcastId = $data['podcast_id']; - } else { - $person = (new PodcastPersonModel())->find( - is_array($data['id']) ? $data['id']['id'] : $data['id'], - ); - $podcastId = $person->podcast_id; - } - - cache()->delete("podcast#{$podcastId}_persons"); - (new PodcastModel())->clearCache(['id' => $podcastId]); - - return $data; - } -} diff --git a/app/Models/SoundbiteModel.php b/app/Models/SoundbiteModel.php index 8bdf04ead2..3c00ae03a7 100644 --- a/app/Models/SoundbiteModel.php +++ b/app/Models/SoundbiteModel.php @@ -66,10 +66,7 @@ class SoundbiteModel extends Model */ protected $beforeDelete = ['clearCache']; - /** - * @return bool|BaseResult - */ - public function deleteSoundbite($podcastId, $episodeId, $soundbiteId) + public function deleteSoundbite(int $podcastId, int $episodeId, int $soundbiteId): BaseResult|bool { return $this->delete([ 'podcast_id' => $podcastId, @@ -99,6 +96,7 @@ class SoundbiteModel extends Model } /** + * @param array<string, array<string|int, mixed>> $data * @return array<string, array<string|int, mixed>> */ public function clearCache(array $data): array diff --git a/app/Models/UserModel.php b/app/Models/UserModel.php index 3cc6d285e1..1d7c7e858c 100644 --- a/app/Models/UserModel.php +++ b/app/Models/UserModel.php @@ -18,7 +18,10 @@ class UserModel extends MythAuthUserModel */ protected $returnType = User::class; - public function getPodcastContributors($podcastId) + /** + * @return User[] + */ + public function getPodcastContributors(int $podcastId): array { $cacheName = "podcast#{$podcastId}_contributors"; if (!($found = cache($cacheName))) { @@ -37,7 +40,7 @@ class UserModel extends MythAuthUserModel return $found; } - public function getPodcastContributor($user_id, $podcast_id) + public function getPodcastContributor(int $userId, int $podcastId): ?User { return $this->select( 'users.*, podcasts_users.podcast_id as podcast_id, auth_groups.name as podcast_role', @@ -45,8 +48,8 @@ class UserModel extends MythAuthUserModel ->join('podcasts_users', 'podcasts_users.user_id = users.id') ->join('auth_groups', 'auth_groups.id = podcasts_users.group_id') ->where([ - 'users.id' => $user_id, - 'podcast_id' => $podcast_id, + 'users.id' => $userId, + 'podcast_id' => $podcastId, ]) ->first(); } diff --git a/app/Validation/FileRules.php b/app/Validation/FileRules.php index 05a7284a63..3ccc42b0d3 100644 --- a/app/Validation/FileRules.php +++ b/app/Validation/FileRules.php @@ -17,7 +17,7 @@ class FileRules extends ValidationFileRules * a specified allowable dimension. * * @param string|null $blank - * + * */ public function min_dims(string $blank = null, string $params): bool { diff --git a/app/Views/errors/cli/error_exception.php b/app/Views/errors/cli/error_exception.php index 8fe9a97a22..14a0907d35 100644 --- a/app/Views/errors/cli/error_exception.php +++ b/app/Views/errors/cli/error_exception.php @@ -4,7 +4,7 @@ use CodeIgniter\CLI\CLI; // The main Exception CLI::newLine(); -CLI::write('[' . get_class($exception) . ']', 'light_gray', 'red'); +CLI::write('[' . $exception::class . ']', 'light_gray', 'red'); CLI::newLine(); CLI::write($message); CLI::newLine(); @@ -58,7 +58,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) { array_map(function ($value) { switch (true) { case is_object($value): - return 'Object(' . get_class($value) . ')'; + return 'Object(' . $value::class . ')'; case is_array($value): return count($value) > 0 ? '[...]' : '[]'; case is_null($value): diff --git a/composer.json b/composer.json index 958cdad1f5..8431bdfe5c 100644 --- a/composer.json +++ b/composer.json @@ -6,15 +6,15 @@ "homepage": "https://castopod.org", "license": "AGPL-3.0-or-later", "require": { - "php": "^7.3||^8.0", + "php": "^8.0", "james-heinrich/getid3": "~2.0.0-dev", "whichbrowser/parser": "^v2.1.1", "geoip2/geoip2": "^v2.11.0", "myth/auth": "dev-develop", "codeigniter4/codeigniter4": "dev-develop", - "league/commonmark": "^1.6.0", + "league/commonmark": "^1.6.2", "vlucas/phpdotenv": "^v5.3.0", - "league/html-to-markdown": "^4.10", + "league/html-to-markdown": "^4.10.0", "opawg/user-agents-php": "^v1.0", "podlibre/ipcat": "^v1.0", "podlibre/podcast-namespace": "^v1.0.6", @@ -24,14 +24,14 @@ }, "require-dev": { "mikey179/vfsstream": "^v1.6.8", - "phpunit/phpunit": "^9.5.1", + "phpunit/phpunit": "^9.5.4", "squizlabs/php_codesniffer": "^3.6.0", "rector/rector": "^0.10.17", "captainhook/captainhook": "^5.9", "captainhook/plugin-composer": "^5.2", "phpstan/phpstan": "^0.12.85", - "phpstan/extension-installer": "^1.1", - "rector/rector-phpstan-rules": "^0.2.1" + "phpstan/extension-installer": "^1.1.0", + "rector/rector-phpstan-rules": "^0.2.6" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index be9d77faca..16611f06cf 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": "cdbc538742689916718b17f15ba7d875", + "content-hash": "d5423c58c26549da4a5a20bfa4e65909", "packages": [ { "name": "brick/math", @@ -68,12 +68,12 @@ "source": { "type": "git", "url": "https://github.com/codeigniter4/CodeIgniter4.git", - "reference": "5713da574bfaa4d73949d84330ccf80758cd6ae4" + "reference": "43e0e9611b7e527a4b927127fc6f26a1c6d123a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/5713da574bfaa4d73949d84330ccf80758cd6ae4", - "reference": "5713da574bfaa4d73949d84330ccf80758cd6ae4", + "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/43e0e9611b7e527a4b927127fc6f26a1c6d123a5", + "reference": "43e0e9611b7e527a4b927127fc6f26a1c6d123a5", "shasum": "" }, "require": { @@ -152,7 +152,7 @@ "slack": "https://codeigniterchat.slack.com", "issues": "https://github.com/codeigniter4/CodeIgniter4/issues" }, - "time": "2021-05-12T10:19:48+00:00" + "time": "2021-05-13T17:30:38+00:00" }, { "name": "composer/ca-bundle", @@ -1196,12 +1196,12 @@ "source": { "type": "git", "url": "https://github.com/lonnieezell/myth-auth.git", - "reference": "2b42da1884745eec22ac10f7941a4f9350576a86" + "reference": "c7f79d1b938e371cfafdc7e3c59c810f2c672727" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/2b42da1884745eec22ac10f7941a4f9350576a86", - "reference": "2b42da1884745eec22ac10f7941a4f9350576a86", + "url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/c7f79d1b938e371cfafdc7e3c59c810f2c672727", + "reference": "c7f79d1b938e371cfafdc7e3c59c810f2c672727", "shasum": "" }, "require": { @@ -1216,7 +1216,7 @@ "fakerphp/faker": "^1.9", "mockery/mockery": "^1.0", "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^9.0", + "phpunit/phpunit": "^9.2", "squizlabs/php_codesniffer": "^3.5" }, "default-branch": true, @@ -1262,7 +1262,7 @@ "type": "patreon" } ], - "time": "2021-05-02T05:32:03+00:00" + "time": "2021-05-13T18:19:37+00:00" }, { "name": "opawg/user-agents-php", @@ -2834,118 +2834,6 @@ ], "time": "2020-11-10T18:47:58+00:00" }, - { - "name": "ergebnis/json-printer", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/ergebnis/json-printer.git", - "reference": "e4190dadd9937a77d8afcaf2b6c42a528ab367d6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/e4190dadd9937a77d8afcaf2b6c42a528ab367d6", - "reference": "e4190dadd9937a77d8afcaf2b6c42a528ab367d6", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "ergebnis/license": "^1.0.0", - "ergebnis/php-cs-fixer-config": "^2.2.1", - "ergebnis/phpstan-rules": "~0.15.2", - "ergebnis/test-util": "^1.1.0", - "infection/infection": "~0.15.3", - "phpstan/extension-installer": "^1.0.4", - "phpstan/phpstan": "~0.12.40", - "phpstan/phpstan-deprecation-rules": "~0.12.5", - "phpstan/phpstan-phpunit": "~0.12.16", - "phpstan/phpstan-strict-rules": "~0.12.4", - "phpunit/phpunit": "^8.5.8", - "psalm/plugin-phpunit": "~0.11.0", - "vimeo/psalm": "^3.14.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ergebnis\\Json\\Printer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Möller", - "email": "am@localheinz.com" - } - ], - "description": "Provides a JSON printer, allowing for flexible indentation.", - "homepage": "https://github.com/ergebnis/json-printer", - "keywords": [ - "formatter", - "json", - "printer" - ], - "support": { - "issues": "https://github.com/ergebnis/json-printer/issues", - "source": "https://github.com/ergebnis/json-printer" - }, - "funding": [ - { - "url": "https://github.com/localheinz", - "type": "github" - } - ], - "time": "2020-08-30T12:17:03+00:00" - }, - { - "name": "idiosyncratic/editorconfig", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/idiosyncratic-code/editorconfig-php.git", - "reference": "50f742daee8b7a632b795f5927d8d88c43dd3a4f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/idiosyncratic-code/editorconfig-php/zipball/50f742daee8b7a632b795f5927d8d88c43dd3a4f", - "reference": "50f742daee8b7a632b795f5927d8d88c43dd3a4f", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "idiosyncratic/devtools": "^0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Idiosyncratic\\EditorConfig\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Jason Silkey", - "email": "jason@jasonsilkey.com" - } - ], - "description": "PHP implementation of EditorConfig", - "support": { - "issues": "https://github.com/idiosyncratic-code/editorconfig-php/issues", - "source": "https://github.com/idiosyncratic-code/editorconfig-php/tree/0.1.1" - }, - "time": "2021-05-03T15:39:40+00:00" - }, { "name": "jean85/pretty-package-versions", "version": "1.6.0", @@ -3313,6 +3201,73 @@ }, "time": "2021-02-28T12:30:32+00:00" }, + { + "name": "nette/robot-loader", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/nette/robot-loader.git", + "reference": "3973cf3970d1de7b30888fd10b92dac9e0c2fd82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/robot-loader/zipball/3973cf3970d1de7b30888fd10b92dac9e0c2fd82", + "reference": "3973cf3970d1de7b30888fd10b92dac9e0c2fd82", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/finder": "^2.5 || ^3.0", + "nette/utils": "^3.0", + "php": ">=7.1" + }, + "require-dev": { + "nette/tester": "^2.0", + "phpstan/phpstan": "^0.12", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🀠Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.", + "homepage": "https://nette.org", + "keywords": [ + "autoload", + "class", + "interface", + "nette", + "trait" + ], + "support": { + "issues": "https://github.com/nette/robot-loader/issues", + "source": "https://github.com/nette/robot-loader/tree/v3.4.0" + }, + "time": "2021-03-07T15:12:01+00:00" + }, { "name": "nette/utils", "version": "v3.2.2", @@ -3400,16 +3355,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.10.4", + "version": "v4.10.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f", + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f", "shasum": "" }, "require": { @@ -3450,9 +3405,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5" }, - "time": "2020-12-20T10:01:03+00:00" + "time": "2021-05-03T19:11:20+00:00" }, { "name": "phar-io/manifest", @@ -3887,16 +3842,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.85", + "version": "0.12.86", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "20e6333c0067875ad7697cd8acdf245c6ef69d03" + "reference": "a84fdc53ecca7643dbc89ef8880d8b393a6c155a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/20e6333c0067875ad7697cd8acdf245c6ef69d03", - "reference": "20e6333c0067875ad7697cd8acdf245c6ef69d03", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a84fdc53ecca7643dbc89ef8880d8b393a6c155a", + "reference": "a84fdc53ecca7643dbc89ef8880d8b393a6c155a", "shasum": "" }, "require": { @@ -3927,7 +3882,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.85" + "source": "https://github.com/phpstan/phpstan/tree/0.12.86" }, "funding": [ { @@ -3943,25 +3898,25 @@ "type": "tidelift" } ], - "time": "2021-04-27T14:13:16+00:00" + "time": "2021-05-08T11:29:01+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "0.12.18", + "version": "0.12.19", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72" + "reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72", - "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/52f7072ddc5f81492f9d2de65a24813a48c90b18", + "reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.60" + "phpstan/phpstan": "^0.12.86" }, "conflict": { "phpunit/phpunit": "<7.0" @@ -3996,9 +3951,9 @@ "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" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.19" }, - "time": "2021-03-06T11:51:27+00:00" + "time": "2021-04-30T11:10:37+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4519,102 +4474,49 @@ }, "time": "2019-01-08T18:20:26+00:00" }, - { - "name": "rector/extension-installer", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/rectorphp/extension-installer.git", - "reference": "56c97630fca170b5586b2f08e76348f924ebb8dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rectorphp/extension-installer/zipball/56c97630fca170b5586b2f08e76348f924ebb8dd", - "reference": "56c97630fca170b5586b2f08e76348f924ebb8dd", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2.0", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "composer/composer": "^2.0", - "composer/xdebug-handler": "2.0 as 1.4", - "friendsofphp/php-cs-fixer": "^3.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", - "symplify/easy-coding-standard": "^9.3.1", - "symplify/phpstan-extensions": "^9.3", - "symplify/phpstan-rules": "^9.3" - }, - "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/extension-installer/issues", - "source": "https://github.com/rectorphp/extension-installer/tree/0.10.2" - }, - "time": "2021-05-06T21:14:19+00:00" - }, { "name": "rector/rector", - "version": "0.10.22", + "version": "0.10.17", "source": { "type": "git", - "url": "https://github.com/rectorphp/rector-src.git", - "reference": "d2b8907ce6fb86dd4d8fe2dd661f348b97e812e3" + "url": "https://github.com/rectorphp/rector.git", + "reference": "ef5167c528cb4b2ca983adaf5a4cccbcad2547b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector-src/zipball/d2b8907ce6fb86dd4d8fe2dd661f348b97e812e3", - "reference": "d2b8907ce6fb86dd4d8fe2dd661f348b97e812e3", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/ef5167c528cb4b2ca983adaf5a4cccbcad2547b7", + "reference": "ef5167c528cb4b2ca983adaf5a4cccbcad2547b7", "shasum": "" }, "require": { "composer/semver": "^3.2", - "composer/xdebug-handler": "^1.3|^2.0", + "composer/xdebug-handler": "^1.4|^2.0", "danielstjules/stringy": "^3.1", "doctrine/inflector": "^2.0", - "ergebnis/json-printer": "^3.1", "ext-dom": "*", "ext-json": "*", - "idiosyncratic/editorconfig": "^0.1.0", "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", + "nikic/php-parser": "^4.10.4", "php": "^7.3|^8.0", "phpstan/phpdoc-parser": "^0.5.4", - "phpstan/phpstan": "0.12.85", + "phpstan/phpstan": "^0.12.83", "phpstan/phpstan-phpunit": "^0.12.18", - "rector/extension-installer": "^0.10.2", "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.9", - "rector/rector-nette-to-symfony": "^0.10.0", + "rector/rector-nette": "^0.10.8", "rector/rector-phpunit": "^0.10.8", "rector/rector-symfony": "^0.10.5", "sebastian/diff": "^4.0.4", - "shanethehat/pretty-xml": "^1.0", "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", @@ -4632,13 +4534,16 @@ "rector/rector-prefixed": "self.version" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^2.18.6", + "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/composer-json-manipulator": "^9.3", "symplify/easy-ci": "^9.3", "symplify/easy-coding-standard": "^9.3", "symplify/easy-testing": "^9.3", @@ -4665,18 +4570,36 @@ "Rector\\Compiler\\": "utils/compiler/src" }, "files": [ - "src/functions/node_helper.php", - "src/constants.php" + "src/functions/node_helper.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": { - "source": "https://github.com/rectorphp/rector-src/tree/0.10.22" + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/0.10.17" }, "funding": [ { @@ -4684,7 +4607,7 @@ "type": "github" } ], - "time": "2021-05-11T22:28:39+00:00" + "time": "2021-05-02T22:55:38+00:00" }, { "name": "rector/rector-cakephp", @@ -4798,6 +4721,58 @@ }, "time": "2021-04-24T12:17:00+00:00" }, + { + "name": "rector/rector-installer", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/extension-installer.git", + "reference": "56c97630fca170b5586b2f08e76348f924ebb8dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/extension-installer/zipball/56c97630fca170b5586b2f08e76348f924ebb8dd", + "reference": "56c97630fca170b5586b2f08e76348f924ebb8dd", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1 || ^2.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "composer/xdebug-handler": "2.0 as 1.4", + "friendsofphp/php-cs-fixer": "^3.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", + "symplify/easy-coding-standard": "^9.3.1", + "symplify/phpstan-extensions": "^9.3", + "symplify/phpstan-rules": "^9.3" + }, + "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/extension-installer/issues", + "source": "https://github.com/rectorphp/extension-installer/tree/0.10.2" + }, + "abandoned": "rector/extension-installer", + "time": "2021-05-06T21:14:19+00:00" + }, { "name": "rector/rector-laravel", "version": "0.10.2", @@ -4856,22 +4831,22 @@ }, { "name": "rector/rector-nette", - "version": "0.10.10", + "version": "0.10.9", "source": { "type": "git", "url": "https://github.com/rectorphp/rector-nette.git", - "reference": "e9f0de1ffb3ba1eecdaa281fcd64fda1d3515e6f" + "reference": "19c85c870f9a7a90d1f2e9be9b3e048b4db69697" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector-nette/zipball/e9f0de1ffb3ba1eecdaa281fcd64fda1d3515e6f", - "reference": "e9f0de1ffb3ba1eecdaa281fcd64fda1d3515e6f", + "url": "https://api.github.com/repos/rectorphp/rector-nette/zipball/19c85c870f9a7a90d1f2e9be9b3e048b4db69697", + "reference": "19c85c870f9a7a90d1f2e9be9b3e048b4db69697", "shasum": "" }, "require": { "ext-xml": "*", "php": ">=7.3", - "rector/rector": "^0.10.19" + "rector/rector": "^0.10.12" }, "conflict": { "rector/rector": "<=0.10.3" @@ -4881,14 +4856,13 @@ "nette/di": "^3.0", "nette/forms": "3.0.*", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^0.12.85", "phpstan/phpstan-nette": "^0.12.16", "phpunit/phpunit": "^9.5", - "rector/rector-phpstan-rules": "^0.2", - "symplify/easy-coding-standard": "^9.3", - "symplify/phpstan-extensions": "^9.3", - "symplify/phpstan-rules": "^9.3", - "symplify/rule-doc-generator": "^9.3" + "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": { @@ -4913,91 +4887,29 @@ "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.10" - }, - "time": "2021-05-06T23:55:02+00:00" - }, - { - "name": "rector/rector-nette-to-symfony", - "version": "0.10.0", - "source": { - "type": "git", - "url": "https://github.com/rectorphp/rector-nette-to-symfony.git", - "reference": "27be9cb982ac7ad3799e0ac7fd45be0a46d7fb0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector-nette-to-symfony/zipball/27be9cb982ac7ad3799e0ac7fd45be0a46d7fb0b", - "reference": "27be9cb982ac7ad3799e0ac7fd45be0a46d7fb0b", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "rector/rector": "^0.10.19", - "rector/rector-nette": "^0.10.9" + "source": "https://github.com/rectorphp/rector-nette/tree/0.10.9" }, - "require-dev": { - "nette/application": "^3.1", - "nette/forms": "^3.1", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-nette": "^0.12.16", - "phpunit/phpunit": "^9.5", - "rector/rector-phpstan-rules": "^0.1", - "symfony/form": "^5.2", - "symplify/easy-coding-standard": "^9.3", - "symplify/phpstan-extensions": "^9.3", - "symplify/phpstan-rules": "^9.3", - "symplify/rule-doc-generator": "^9.3" - }, - "type": "rector-extension", - "extra": { - "rector": { - "includes": [ - "config/config.php" - ] - } - }, - "autoload": { - "psr-4": { - "Rector\\NetteToSymfony\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Generate Rector rules from command line", - "support": { - "issues": "https://github.com/rectorphp/rector-nette-to-symfony/issues", - "source": "https://github.com/rectorphp/rector-nette-to-symfony/tree/0.10.0" - }, - "funding": [ - { - "url": "https://github.com/tomasvotruba", - "type": "github" - } - ], - "time": "2021-05-05T21:07:31+00:00" + "time": "2021-04-26T10:35:59+00:00" }, { "name": "rector/rector-phpstan-rules", - "version": "0.2.1", + "version": "0.2.8", "source": { "type": "git", "url": "https://github.com/rectorphp/phpstan-rules.git", - "reference": "6f43a35676463c4688e120194d56928c4be523bb" + "reference": "765d8bf702a3928a155be02d3b7413a16b46bf95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/phpstan-rules/zipball/6f43a35676463c4688e120194d56928c4be523bb", - "reference": "6f43a35676463c4688e120194d56928c4be523bb", + "url": "https://api.github.com/repos/rectorphp/phpstan-rules/zipball/765d8bf702a3928a155be02d3b7413a16b46bf95", + "reference": "765d8bf702a3928a155be02d3b7413a16b46bf95", "shasum": "" }, "require": { "nette/utils": "^3.2", "php": ">=7.3", - "phpstan/phpstan": "^0.12.84", - "symplify/phpstan-rules": "^9.2.22" + "phpstan/phpstan": "^0.12.86", + "symplify/phpstan-rules": "^9.3.5" }, "require-dev": { "phpstan/extension-installer": "^1.1", @@ -5015,7 +4927,7 @@ }, "autoload": { "psr-4": { - "Rector\\RectorPHPStanRules\\": "src" + "Rector\\PHPStanRules\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5025,9 +4937,9 @@ "description": "PHPStan rules for Rector projects - with focus on static reflection, constant re-use and Rector design patterns", "support": { "issues": "https://github.com/rectorphp/phpstan-rules/issues", - "source": "https://github.com/rectorphp/phpstan-rules/tree/0.2.1" + "source": "https://github.com/rectorphp/phpstan-rules/tree/0.2.8" }, - "time": "2021-05-06T23:51:11+00:00" + "time": "2021-05-14T08:39:51+00:00" }, { "name": "rector/rector-phpunit", @@ -6281,53 +6193,6 @@ ], "time": "2021-04-10T08:31:02+00:00" }, - { - "name": "shanethehat/pretty-xml", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/shanethehat/pretty-xml.git", - "reference": "2b063c6544c8dc9563c53cb72eb06d1d74c9e75f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/shanethehat/pretty-xml/zipball/2b063c6544c8dc9563c53cb72eb06d1d74c9e75f", - "reference": "2b063c6544c8dc9563c53cb72eb06d1d74c9e75f", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~3.0", - "bossa/phpspec2-expect": "*", - "phpspec/phpspec": "~2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "PrettyXml": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Shane Auckland", - "email": "shane.auckland@gmail.com", - "homepage": "http://shaneauckland.co.uk" - } - ], - "description": "Library for pretty-printing XML", - "keywords": [ - "pretty", - "xml" - ], - "support": { - "issues": "https://github.com/shanethehat/pretty-xml/issues", - "source": "https://github.com/shanethehat/pretty-xml/tree/master" - }, - "time": "2015-08-10T14:22:54+00:00" - }, { "name": "squizlabs/php_codesniffer", "version": "3.6.0", @@ -6386,16 +6251,16 @@ }, { "name": "symfony/config", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8" + "reference": "8dfa5f8adea9cd5155920069224beb04f11d6b7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/3817662ada105c8c4d1afdb4ec003003efd1d8d8", - "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8", + "url": "https://api.github.com/repos/symfony/config/zipball/8dfa5f8adea9cd5155920069224beb04f11d6b7e", + "reference": "8dfa5f8adea9cd5155920069224beb04f11d6b7e", "shasum": "" }, "require": { @@ -6444,7 +6309,7 @@ "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" + "source": "https://github.com/symfony/config/tree/v5.2.8" }, "funding": [ { @@ -6460,20 +6325,20 @@ "type": "tidelift" } ], - "time": "2021-04-07T16:07:52+00:00" + "time": "2021-05-07T13:41:16+00:00" }, { "name": "symfony/console", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629" + "reference": "864568fdc0208b3eba3638b6000b69d2386e6768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/90374b8ed059325b49a29b55b3f8bb4062c87629", - "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629", + "url": "https://api.github.com/repos/symfony/console/zipball/864568fdc0208b3eba3638b6000b69d2386e6768", + "reference": "864568fdc0208b3eba3638b6000b69d2386e6768", "shasum": "" }, "require": { @@ -6541,7 +6406,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.7" + "source": "https://github.com/symfony/console/tree/v5.2.8" }, "funding": [ { @@ -6557,20 +6422,20 @@ "type": "tidelift" } ], - "time": "2021-04-19T14:07:32+00:00" + "time": "2021-05-11T15:45:21+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577" + "reference": "024e929da5a994cbab0ce2291d332f7edf926acf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6ca378b99e3c9ba6127eb43b68389fb2b7348577", - "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/024e929da5a994cbab0ce2291d332f7edf926acf", + "reference": "024e929da5a994cbab0ce2291d332f7edf926acf", "shasum": "" }, "require": { @@ -6628,7 +6493,7 @@ "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" + "source": "https://github.com/symfony/dependency-injection/tree/v5.2.8" }, "funding": [ { @@ -6644,7 +6509,7 @@ "type": "tidelift" } ], - "time": "2021-04-24T14:32:26+00:00" + "time": "2021-05-11T16:07:35+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6715,16 +6580,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "ea3ddbf67615e883ca7c33a4de61213789846782" + "reference": "1416bc16317a8188aabde251afef7618bf4687ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/ea3ddbf67615e883ca7c33a4de61213789846782", - "reference": "ea3ddbf67615e883ca7c33a4de61213789846782", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1416bc16317a8188aabde251afef7618bf4687ac", + "reference": "1416bc16317a8188aabde251afef7618bf4687ac", "shasum": "" }, "require": { @@ -6764,7 +6629,7 @@ "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" + "source": "https://github.com/symfony/error-handler/tree/v5.2.8" }, "funding": [ { @@ -6780,7 +6645,7 @@ "type": "tidelift" } ], - "time": "2021-04-07T15:57:33+00:00" + "time": "2021-05-07T13:42:21+00:00" }, { "name": "symfony/event-dispatcher", @@ -7010,16 +6875,16 @@ }, { "name": "symfony/finder", - "version": "v5.2.4", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "0d639a0943822626290d169965804f79400e6a04" + "reference": "eccb8be70d7a6a2230d05f6ecede40f3fdd9e252" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", - "reference": "0d639a0943822626290d169965804f79400e6a04", + "url": "https://api.github.com/repos/symfony/finder/zipball/eccb8be70d7a6a2230d05f6ecede40f3fdd9e252", + "reference": "eccb8be70d7a6a2230d05f6ecede40f3fdd9e252", "shasum": "" }, "require": { @@ -7051,7 +6916,7 @@ "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" + "source": "https://github.com/symfony/finder/tree/v5.2.8" }, "funding": [ { @@ -7067,7 +6932,7 @@ "type": "tidelift" } ], - "time": "2021-02-15T18:55:04+00:00" + "time": "2021-05-10T14:39:23+00:00" }, { "name": "symfony/http-client-contracts", @@ -7149,16 +7014,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f" + "reference": "e8fbbab7c4a71592985019477532629cb2e142dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f", - "reference": "a416487a73bb9c9d120e9ba3a60547f4a3fb7a1f", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e8fbbab7c4a71592985019477532629cb2e142dc", + "reference": "e8fbbab7c4a71592985019477532629cb2e142dc", "shasum": "" }, "require": { @@ -7202,7 +7067,7 @@ "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" + "source": "https://github.com/symfony/http-foundation/tree/v5.2.8" }, "funding": [ { @@ -7218,20 +7083,20 @@ "type": "tidelift" } ], - "time": "2021-05-01T13:46:24+00:00" + "time": "2021-05-07T13:41:16+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "1e9f6879f070f718e0055fbac232a56f67b8b6bd" + "reference": "c3cb71ee7e2d3eae5fe1001f81780d6a49b37937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1e9f6879f070f718e0055fbac232a56f67b8b6bd", - "reference": "1e9f6879f070f718e0055fbac232a56f67b8b6bd", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c3cb71ee7e2d3eae5fe1001f81780d6a49b37937", + "reference": "c3cb71ee7e2d3eae5fe1001f81780d6a49b37937", "shasum": "" }, "require": { @@ -7314,7 +7179,7 @@ "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" + "source": "https://github.com/symfony/http-kernel/tree/v5.2.8" }, "funding": [ { @@ -7330,7 +7195,7 @@ "type": "tidelift" } ], - "time": "2021-05-01T14:53:15+00:00" + "time": "2021-05-12T13:27:53+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -7719,16 +7584,16 @@ }, { "name": "symfony/string", - "version": "v5.2.6", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" + "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", - "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "url": "https://api.github.com/repos/symfony/string/zipball/01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", + "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", "shasum": "" }, "require": { @@ -7782,7 +7647,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.6" + "source": "https://github.com/symfony/string/tree/v5.2.8" }, "funding": [ { @@ -7798,20 +7663,20 @@ "type": "tidelift" } ], - "time": "2021-03-17T17:12:15+00:00" + "time": "2021-05-10T14:56:10+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.2.7", + "version": "v5.2.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "27cb9f7cfa3853c736425c7233a8f68814b19636" + "reference": "d693200a73fae179d27f8f1b16b4faf3e8569eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/27cb9f7cfa3853c736425c7233a8f68814b19636", - "reference": "27cb9f7cfa3853c736425c7233a8f68814b19636", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d693200a73fae179d27f8f1b16b4faf3e8569eba", + "reference": "d693200a73fae179d27f8f1b16b4faf3e8569eba", "shasum": "" }, "require": { @@ -7870,7 +7735,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.2.7" + "source": "https://github.com/symfony/var-dumper/tree/v5.2.8" }, "funding": [ { @@ -7886,34 +7751,34 @@ "type": "tidelift" } ], - "time": "2021-04-19T14:07:32+00:00" + "time": "2021-05-07T13:42:21+00:00" }, { "name": "symplify/astral", - "version": "v9.3.4", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/astral.git", - "reference": "2ec71b4aad8995c526eee88c3d040bd7a03523e7" + "reference": "4934e1f0fef054051441db7ecaa267bd80231e4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/astral/zipball/2ec71b4aad8995c526eee88c3d040bd7a03523e7", - "reference": "2ec71b4aad8995c526eee88c3d040bd7a03523e7", + "url": "https://api.github.com/repos/symplify/astral/zipball/4934e1f0fef054051441db7ecaa267bd80231e4f", + "reference": "4934e1f0fef054051441db7ecaa267bd80231e4f", "shasum": "" }, "require": { "nette/utils": "^3.2", - "nikic/php-parser": "4.10.4", + "nikic/php-parser": "4.10.5", "php": ">=7.3", "symfony/dependency-injection": "^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/autowire-array-parameter": "^9.3.4", - "symplify/package-builder": "^9.3.4" + "symplify/autowire-array-parameter": "^9.3.11", + "symplify/package-builder": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5", - "symplify/easy-testing": "^9.3.4" + "symplify/easy-testing": "^9.3.11" }, "type": "library", "extra": { @@ -7932,7 +7797,7 @@ ], "description": "Toolking for smart daily work with AST", "support": { - "source": "https://github.com/symplify/astral/tree/v9.3.4" + "source": "https://github.com/symplify/astral/tree/v9.3.11" }, "funding": [ { @@ -7944,27 +7809,27 @@ "type": "github" } ], - "time": "2021-05-11T13:35:47+00:00" + "time": "2021-05-13T11:33:59+00:00" }, { "name": "symplify/autowire-array-parameter", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/autowire-array-parameter.git", - "reference": "00044807ab4b8ae7df610f80d54360a49b253146" + "reference": "269fcb76ed64ca1e9dd0abe6c13273be8dfba64c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/autowire-array-parameter/zipball/00044807ab4b8ae7df610f80d54360a49b253146", - "reference": "00044807ab4b8ae7df610f80d54360a49b253146", + "url": "https://api.github.com/repos/symplify/autowire-array-parameter/zipball/269fcb76ed64ca1e9dd0abe6c13273be8dfba64c", + "reference": "269fcb76ed64ca1e9dd0abe6c13273be8dfba64c", "shasum": "" }, "require": { "nette/utils": "^3.2", "php": ">=7.3", "symfony/dependency-injection": "^5.2", - "symplify/package-builder": "^9.3.6" + "symplify/package-builder": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -7986,7 +7851,7 @@ ], "description": "Autowire array parameters for your Symfony applications", "support": { - "source": "https://github.com/symplify/autowire-array-parameter/tree/v9.3.6" + "source": "https://github.com/symplify/autowire-array-parameter/tree/v9.3.11" }, "funding": [ { @@ -7998,20 +7863,20 @@ "type": "github" } ], - "time": "2021-05-11T22:44:31+00:00" + "time": "2021-05-13T11:33:56+00:00" }, { "name": "symplify/composer-json-manipulator", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/composer-json-manipulator.git", - "reference": "c5218020d999c775f5bcc625c7e3d46f7bd21e81" + "reference": "87a675a761aa5df90f5d5cf2c7d49d1e7e7a91be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/composer-json-manipulator/zipball/c5218020d999c775f5bcc625c7e3d46f7bd21e81", - "reference": "c5218020d999c775f5bcc625c7e3d46f7bd21e81", + "url": "https://api.github.com/repos/symplify/composer-json-manipulator/zipball/87a675a761aa5df90f5d5cf2c7d49d1e7e7a91be", + "reference": "87a675a761aa5df90f5d5cf2c7d49d1e7e7a91be", "shasum": "" }, "require": { @@ -8021,8 +7886,8 @@ "symfony/dependency-injection": "^5.2", "symfony/filesystem": "^4.4|^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/package-builder": "^9.3.6", - "symplify/smart-file-system": "^9.3.6" + "symplify/package-builder": "^9.3.11", + "symplify/smart-file-system": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8044,7 +7909,7 @@ ], "description": "Package to load, merge and save composer.json file(s)", "support": { - "source": "https://github.com/symplify/composer-json-manipulator/tree/v9.3.6" + "source": "https://github.com/symplify/composer-json-manipulator/tree/v9.3.11" }, "funding": [ { @@ -8056,20 +7921,20 @@ "type": "github" } ], - "time": "2021-05-11T22:44:23+00:00" + "time": "2021-05-13T11:33:58+00:00" }, { "name": "symplify/console-color-diff", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/console-color-diff.git", - "reference": "8f25607a0b05754bbf6ac8f06d6662adf201e1cb" + "reference": "7b97c8e124eb790a24b2e3319e3795abeffef557" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/console-color-diff/zipball/8f25607a0b05754bbf6ac8f06d6662adf201e1cb", - "reference": "8f25607a0b05754bbf6ac8f06d6662adf201e1cb", + "url": "https://api.github.com/repos/symplify/console-color-diff/zipball/7b97c8e124eb790a24b2e3319e3795abeffef557", + "reference": "7b97c8e124eb790a24b2e3319e3795abeffef557", "shasum": "" }, "require": { @@ -8079,7 +7944,7 @@ "symfony/console": "^4.4|^5.2", "symfony/dependency-injection": "^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/package-builder": "^9.3.6" + "symplify/package-builder": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8101,7 +7966,7 @@ ], "description": "Package to print diffs in console with colors", "support": { - "source": "https://github.com/symplify/console-color-diff/tree/v9.3.6" + "source": "https://github.com/symplify/console-color-diff/tree/v9.3.11" }, "funding": [ { @@ -8113,32 +7978,32 @@ "type": "github" } ], - "time": "2021-05-11T22:44:55+00:00" + "time": "2021-05-13T11:34:04+00:00" }, { "name": "symplify/console-package-builder", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/console-package-builder.git", - "reference": "4a7d5d412e2bdf805c4603a02c69ef16183521c9" + "reference": "4fd36633c3607f74a21eee5bd32c00f24130261f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/console-package-builder/zipball/4a7d5d412e2bdf805c4603a02c69ef16183521c9", - "reference": "4a7d5d412e2bdf805c4603a02c69ef16183521c9", + "url": "https://api.github.com/repos/symplify/console-package-builder/zipball/4fd36633c3607f74a21eee5bd32c00f24130261f", + "reference": "4fd36633c3607f74a21eee5bd32c00f24130261f", "shasum": "" }, "require": { "php": ">=7.3", "symfony/console": "^4.4|^5.2", "symfony/dependency-injection": "^5.2", - "symplify/symplify-kernel": "^9.3.6" + "symplify/symplify-kernel": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5", "symfony/http-kernel": "^4.4|^5.2", - "symplify/package-builder": "^9.3.6" + "symplify/package-builder": "^9.3.11" }, "type": "library", "extra": { @@ -8157,22 +8022,22 @@ ], "description": "Package to speed up building command line applications", "support": { - "source": "https://github.com/symplify/console-package-builder/tree/v9.3.6" + "source": "https://github.com/symplify/console-package-builder/tree/v9.3.11" }, - "time": "2021-05-11T22:45:10+00:00" + "time": "2021-05-13T11:33:59+00:00" }, { "name": "symplify/easy-testing", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/easy-testing.git", - "reference": "f8b617e6c0df1c76acb3150af40bd256bf2c0aba" + "reference": "5662ac6e55dca7d7ae7df0b47e84a165573359ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/easy-testing/zipball/f8b617e6c0df1c76acb3150af40bd256bf2c0aba", - "reference": "f8b617e6c0df1c76acb3150af40bd256bf2c0aba", + "url": "https://api.github.com/repos/symplify/easy-testing/zipball/5662ac6e55dca7d7ae7df0b47e84a165573359ed", + "reference": "5662ac6e55dca7d7ae7df0b47e84a165573359ed", "shasum": "" }, "require": { @@ -8182,10 +8047,10 @@ "symfony/dependency-injection": "^5.2", "symfony/finder": "^4.4|^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/console-package-builder": "^9.3.6", - "symplify/package-builder": "^9.3.6", - "symplify/smart-file-system": "^9.3.6", - "symplify/symplify-kernel": "^9.3.6" + "symplify/console-package-builder": "^9.3.11", + "symplify/package-builder": "^9.3.11", + "symplify/smart-file-system": "^9.3.11", + "symplify/symplify-kernel": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8210,7 +8075,7 @@ ], "description": "Testing made easy", "support": { - "source": "https://github.com/symplify/easy-testing/tree/v9.3.6" + "source": "https://github.com/symplify/easy-testing/tree/v9.3.11" }, "funding": [ { @@ -8222,20 +8087,20 @@ "type": "github" } ], - "time": "2021-05-11T22:44:47+00:00" + "time": "2021-05-13T11:34:12+00:00" }, { "name": "symplify/package-builder", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/package-builder.git", - "reference": "9d572946f2483150aae1f7b7d5afe75c5206ffda" + "reference": "479752e9b19efbd0470aba8e92b9d6a01722430b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/package-builder/zipball/9d572946f2483150aae1f7b7d5afe75c5206ffda", - "reference": "9d572946f2483150aae1f7b7d5afe75c5206ffda", + "url": "https://api.github.com/repos/symplify/package-builder/zipball/479752e9b19efbd0470aba8e92b9d6a01722430b", + "reference": "479752e9b19efbd0470aba8e92b9d6a01722430b", "shasum": "" }, "require": { @@ -8247,8 +8112,8 @@ "symfony/dependency-injection": "^5.2", "symfony/finder": "^4.4|^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/easy-testing": "^9.3.6", - "symplify/symplify-kernel": "^9.3.6" + "symplify/easy-testing": "^9.3.11", + "symplify/symplify-kernel": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8270,7 +8135,7 @@ ], "description": "Dependency Injection, Console and Kernel toolkit for Symplify packages.", "support": { - "source": "https://github.com/symplify/package-builder/tree/v9.3.6" + "source": "https://github.com/symplify/package-builder/tree/v9.3.11" }, "funding": [ { @@ -8282,33 +8147,33 @@ "type": "github" } ], - "time": "2021-05-11T22:45:02+00:00" + "time": "2021-05-13T11:34:38+00:00" }, { "name": "symplify/phpstan-rules", - "version": "v9.3.4", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/phpstan-rules.git", - "reference": "4856ef701dcb0823f7455441201f11f378b99c97" + "reference": "e556dc413fc10804706d57b324e9aa14513c5f57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/phpstan-rules/zipball/4856ef701dcb0823f7455441201f11f378b99c97", - "reference": "4856ef701dcb0823f7455441201f11f378b99c97", + "url": "https://api.github.com/repos/symplify/phpstan-rules/zipball/e556dc413fc10804706d57b324e9aa14513c5f57", + "reference": "e556dc413fc10804706d57b324e9aa14513c5f57", "shasum": "" }, "require": { "nette/utils": "^3.2", - "nikic/php-parser": "4.10.4", + "nikic/php-parser": "4.10.5", "php": ">=7.3", "phpstan/phpdoc-parser": "^0.5", - "phpstan/phpstan": "0.12.85", - "symplify/astral": "^9.3.4", - "symplify/composer-json-manipulator": "^9.3.4", - "symplify/package-builder": "^9.3.4", - "symplify/rule-doc-generator-contracts": "^9.3.4", - "symplify/smart-file-system": "^9.3.4", + "phpstan/phpstan": "0.12.86", + "symplify/astral": "^9.3.11", + "symplify/composer-json-manipulator": "^9.3.11", + "symplify/package-builder": "^9.3.11", + "symplify/rule-doc-generator-contracts": "^9.3.11", + "symplify/smart-file-system": "^9.3.11", "webmozart/assert": "^1.9" }, "require-dev": { @@ -8316,9 +8181,9 @@ "nette/forms": "^3.1", "phpunit/phpunit": "^9.5", "symfony/framework-bundle": "^4.4|^5.2", - "symplify/easy-testing": "^9.3.4", - "symplify/phpstan-extensions": "^9.3.4", - "symplify/rule-doc-generator": "^9.3.4" + "symplify/easy-testing": "^9.3.11", + "symplify/phpstan-extensions": "^9.3.11", + "symplify/rule-doc-generator": "^9.3.11" }, "type": "phpstan-extension", "extra": { @@ -8346,7 +8211,7 @@ ], "description": "Set of Symplify rules for PHPStan", "support": { - "source": "https://github.com/symplify/phpstan-rules/tree/v9.3.4" + "source": "https://github.com/symplify/phpstan-rules/tree/v9.3.11" }, "funding": [ { @@ -8358,11 +8223,11 @@ "type": "github" } ], - "time": "2021-05-11T13:36:44+00:00" + "time": "2021-05-13T11:34:44+00:00" }, { "name": "symplify/rule-doc-generator-contracts", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/rule-doc-generator-contracts.git", @@ -8395,7 +8260,7 @@ ], "description": "Contracts for production code of RuleDocGenerator", "support": { - "source": "https://github.com/symplify/rule-doc-generator-contracts/tree/v9.3.6" + "source": "https://github.com/symplify/rule-doc-generator-contracts/tree/v9.3.11" }, "funding": [ { @@ -8411,16 +8276,16 @@ }, { "name": "symplify/set-config-resolver", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/set-config-resolver.git", - "reference": "7da336c9144b3bfea439e8335680c2abe57cdca1" + "reference": "cced883469d32b45f83471c17443faf18a3c2e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/set-config-resolver/zipball/7da336c9144b3bfea439e8335680c2abe57cdca1", - "reference": "7da336c9144b3bfea439e8335680c2abe57cdca1", + "url": "https://api.github.com/repos/symplify/set-config-resolver/zipball/cced883469d32b45f83471c17443faf18a3c2e75", + "reference": "cced883469d32b45f83471c17443faf18a3c2e75", "shasum": "" }, "require": { @@ -8431,8 +8296,8 @@ "symfony/dependency-injection": "^5.2", "symfony/filesystem": "^4.4|^5.2", "symfony/finder": "^4.4|^5.2", - "symplify/smart-file-system": "^9.3.6", - "symplify/symplify-kernel": "^9.3.6" + "symplify/smart-file-system": "^9.3.11", + "symplify/symplify-kernel": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8454,7 +8319,7 @@ ], "description": "Resolve config and sets from configs and cli opptions for CLI applications", "support": { - "source": "https://github.com/symplify/set-config-resolver/tree/v9.3.6" + "source": "https://github.com/symplify/set-config-resolver/tree/v9.3.11" }, "funding": [ { @@ -8466,20 +8331,20 @@ "type": "github" } ], - "time": "2021-05-11T22:45:21+00:00" + "time": "2021-05-13T11:34:45+00:00" }, { "name": "symplify/simple-php-doc-parser", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/simple-php-doc-parser.git", - "reference": "cea820fa5a20d2fa8a240e2e0fcd7b063ce21b9d" + "reference": "e70450dfaa94db70e34a4d2252891a43bc57f956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/simple-php-doc-parser/zipball/cea820fa5a20d2fa8a240e2e0fcd7b063ce21b9d", - "reference": "cea820fa5a20d2fa8a240e2e0fcd7b063ce21b9d", + "url": "https://api.github.com/repos/symplify/simple-php-doc-parser/zipball/e70450dfaa94db70e34a4d2252891a43bc57f956", + "reference": "e70450dfaa94db70e34a4d2252891a43bc57f956", "shasum": "" }, "require": { @@ -8488,11 +8353,11 @@ "symfony/config": "^4.4|^5.2", "symfony/dependency-injection": "^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/package-builder": "^9.3.6" + "symplify/package-builder": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5", - "symplify/easy-testing": "^9.3.6" + "symplify/easy-testing": "^9.3.11" }, "type": "library", "extra": { @@ -8511,7 +8376,7 @@ ], "description": "Service integration of phpstan/phpdoc-parser, with few extra goodies for practical simple use", "support": { - "source": "https://github.com/symplify/simple-php-doc-parser/tree/v9.3.6" + "source": "https://github.com/symplify/simple-php-doc-parser/tree/v9.3.11" }, "funding": [ { @@ -8523,20 +8388,20 @@ "type": "github" } ], - "time": "2021-05-11T22:45:32+00:00" + "time": "2021-05-13T11:34:46+00:00" }, { "name": "symplify/skipper", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/skipper.git", - "reference": "67417fd44b4ddc3796df5c699ecd076b8c71927e" + "reference": "3b446ce9c78f0d455788d9bfd92915eac6da1054" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/skipper/zipball/67417fd44b4ddc3796df5c699ecd076b8c71927e", - "reference": "67417fd44b4ddc3796df5c699ecd076b8c71927e", + "url": "https://api.github.com/repos/symplify/skipper/zipball/3b446ce9c78f0d455788d9bfd92915eac6da1054", + "reference": "3b446ce9c78f0d455788d9bfd92915eac6da1054", "shasum": "" }, "require": { @@ -8546,9 +8411,9 @@ "symfony/dependency-injection": "^5.2", "symfony/filesystem": "^4.4|^5.2", "symfony/finder": "^4.4|^5.2", - "symplify/package-builder": "^9.3.6", - "symplify/smart-file-system": "^9.3.6", - "symplify/symplify-kernel": "^9.3.6" + "symplify/package-builder": "^9.3.11", + "symplify/smart-file-system": "^9.3.11", + "symplify/symplify-kernel": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8570,7 +8435,7 @@ ], "description": "Skip files by rule class, directory, file or fnmatch", "support": { - "source": "https://github.com/symplify/skipper/tree/v9.3.6" + "source": "https://github.com/symplify/skipper/tree/v9.3.11" }, "funding": [ { @@ -8582,11 +8447,11 @@ "type": "github" } ], - "time": "2021-05-11T22:45:36+00:00" + "time": "2021-05-13T11:34:52+00:00" }, { "name": "symplify/smart-file-system", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/smart-file-system.git", @@ -8625,7 +8490,7 @@ ], "description": "Sanitized FileInfo with safe getRealPath() and other handy methods", "support": { - "source": "https://github.com/symplify/smart-file-system/tree/v9.3.6" + "source": "https://github.com/symplify/smart-file-system/tree/v9.3.11" }, "funding": [ { @@ -8641,23 +8506,23 @@ }, { "name": "symplify/symfony-php-config", - "version": "v9.3.5", + "version": "v9.3.10", "source": { "type": "git", "url": "https://github.com/symplify/symfony-php-config.git", - "reference": "9dc9a4010871f822394628487cbc5721c83fe3de" + "reference": "91f29a210de56fb208da5e18c8573937bf7bc37e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/symfony-php-config/zipball/9dc9a4010871f822394628487cbc5721c83fe3de", - "reference": "9dc9a4010871f822394628487cbc5721c83fe3de", + "url": "https://api.github.com/repos/symplify/symfony-php-config/zipball/91f29a210de56fb208da5e18c8573937bf7bc37e", + "reference": "91f29a210de56fb208da5e18c8573937bf7bc37e", "shasum": "" }, "require": { "php": ">=7.3", "symfony/dependency-injection": "^5.2", - "symplify/package-builder": "^9.3.5", - "symplify/symplify-kernel": "^9.3.5" + "symplify/package-builder": "^9.3.10", + "symplify/symplify-kernel": "^9.3.10" }, "require-dev": { "phpstan/phpstan": "0.12.86", @@ -8681,22 +8546,22 @@ ], "description": "Tools that easy work with Symfony PHP Configs", "support": { - "source": "https://github.com/symplify/symfony-php-config/tree/v9.3.5" + "source": "https://github.com/symplify/symfony-php-config/tree/v9.3.10" }, - "time": "2021-05-11T14:11:22+00:00" + "time": "2021-05-12T14:29:08+00:00" }, { "name": "symplify/symplify-kernel", - "version": "v9.3.6", + "version": "v9.3.11", "source": { "type": "git", "url": "https://github.com/symplify/symplify-kernel.git", - "reference": "ba7f3b8ee3ce80a8543f248495a0999e0ea6af7a" + "reference": "d79a26c90ebd292d8b474dbee26b602379be3f66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/symplify-kernel/zipball/ba7f3b8ee3ce80a8543f248495a0999e0ea6af7a", - "reference": "ba7f3b8ee3ce80a8543f248495a0999e0ea6af7a", + "url": "https://api.github.com/repos/symplify/symplify-kernel/zipball/d79a26c90ebd292d8b474dbee26b602379be3f66", + "reference": "d79a26c90ebd292d8b474dbee26b602379be3f66", "shasum": "" }, "require": { @@ -8704,10 +8569,10 @@ "symfony/console": "^4.4|^5.2", "symfony/dependency-injection": "^5.2", "symfony/http-kernel": "^4.4|^5.2", - "symplify/autowire-array-parameter": "^9.3.6", - "symplify/composer-json-manipulator": "^9.3.6", - "symplify/package-builder": "^9.3.6", - "symplify/smart-file-system": "^9.3.6" + "symplify/autowire-array-parameter": "^9.3.11", + "symplify/composer-json-manipulator": "^9.3.11", + "symplify/package-builder": "^9.3.11", + "symplify/smart-file-system": "^9.3.11" }, "require-dev": { "phpunit/phpunit": "^9.5" @@ -8729,9 +8594,9 @@ ], "description": "Internal Kernel for Symplify packages", "support": { - "source": "https://github.com/symplify/symplify-kernel/tree/v9.3.6" + "source": "https://github.com/symplify/symplify-kernel/tree/v9.3.11" }, - "time": "2021-05-11T22:45:48+00:00" + "time": "2021-05-13T11:35:17+00:00" }, { "name": "theseer/tokenizer", @@ -8927,7 +8792,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.3||^8.0" + "php": "^8.0" }, "platform-dev": [], "plugin-api-version": "2.0.0" diff --git a/phpstan.neon b/phpstan.neon index d942305775..e90f864fe3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,9 +1,9 @@ parameters: tmpDir: build/phpstan - level: 5 + level: 6 paths: - app - - tests + # - tests bootstrapFiles: - vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php scanDirectories: @@ -17,10 +17,8 @@ parameters: - app/Libraries/Analytics/Config/Routes.php - app/Views/* ignoreErrors: - - '#Access to property [\$a-zA-Z]+ on an unknown class Myth\\Auth.*#' # TODO: remove when https://github.com/lonnieezell/myth-auth/pull/347 is merged - - '#Call to method [\a-zA-Z]+\(\) on an unknown class Myth\\Auth.*#' # to remove as well - - '#^Access to an undefined property ActivityPub\\Entities\\Actor#' - - '#^Access to an undefined property ActivityPub\\Entities\\Note#' + - '#This property type might be inlined to PHP. Do you have confidence it is correct\? Put it here#' + - '#Function \"preg_.*\(\)\" cannot be used/left in the code#' - '#.* is forbidden to use#' - '#^Cognitive complexity for#' - '#^Class cognitive complexity is#' diff --git a/public/index.php b/public/index.php index b48871b015..a0451b7dfd 100644 --- a/public/index.php +++ b/public/index.php @@ -1,7 +1,9 @@ <?php +use Config\Paths; + // Valid PHP Version? -$minPHPVersion = '7.3'; +$minPHPVersion = '8.0'; if (version_compare(PHP_VERSION, $minPHPVersion, '<')) { die( "Your PHP version must be {$minPHPVersion} or higher to run CodeIgniter. Current version: " . @@ -31,7 +33,7 @@ require realpath(FCPATH . '../app/Config/Paths.php') ?: FCPATH . '../app/Config/Paths.php'; // ^^^ Change this if you move your application folder -$paths = new Config\Paths(); +$paths = new Paths(); // Location of the framework bootstrap file. $bootstrap = diff --git a/rector.php b/rector.php index 9403850fbd..e3ba7ecf78 100644 --- a/rector.php +++ b/rector.php @@ -11,6 +11,7 @@ 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\Php80\Rector\ClassMethod\OptionalParametersAfterRequiredRector; use Rector\Set\ValueObject\SetList; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -24,8 +25,13 @@ return static function (ContainerConfigurator $containerConfigurator): void { __DIR__ . '/public', ]); + // do you need to include constants, class aliases or custom autoloader? files listed will be executed + $parameters->set(Option::BOOTSTRAP_FILES, [ + __DIR__ . '/vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php', + ]); + // Define what rule sets will be applied - $containerConfigurator->import(SetList::PHP_73); + $containerConfigurator->import(SetList::PHP_80); $containerConfigurator->import(SetList::TYPE_DECLARATION); $containerConfigurator->import(SetList::TYPE_DECLARATION_STRICT); $containerConfigurator->import(SetList::CODE_QUALITY); @@ -36,8 +42,8 @@ return static function (ContainerConfigurator $containerConfigurator): void { // 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::ENABLE_CACHE, true); + $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80); $parameters->set(Option::SKIP, [ // skip specific generated files @@ -54,8 +60,17 @@ return static function (ContainerConfigurator $containerConfigurator): void { StringClassNameToClassConstantRector::class => [ __DIR__ . '/app/Language/*', ], + OptionalParametersAfterRequiredRector::class => [ + __DIR__ . '/app/Validation', + ], ]); + // Path to phpstan with extensions, that PHPSTan in Rector uses to determine types + $parameters->set( + Option::PHPSTAN_FOR_RECTOR_PATH, + __DIR__ . '/phpstan.neon', + ); + $services = $containerConfigurator->services(); $services->set(ConsistentPregDelimiterRector::class)->call('configure', [ [ diff --git a/spark b/spark index 83a5cc25c3..6446786fd6 100644 --- a/spark +++ b/spark @@ -1,7 +1,7 @@ #!/usr/bin/env php <?php // Valid PHP Version? -$minPHPVersion = "7.3"; +$minPHPVersion = "8.0"; if (version_compare(PHP_VERSION, $minPHPVersion, "<")) { die( "Your PHP version must be {$minPHPVersion} or higher to run CodeIgniter. Current version: " . diff --git a/tests/unit/HealthTest.php b/tests/unit/HealthTest.php index 30ae5219ab..28b64065e1 100644 --- a/tests/unit/HealthTest.php +++ b/tests/unit/HealthTest.php @@ -2,11 +2,12 @@ namespace Tests\Unit; +use CodeIgniter\Test\CIUnitTestCase; use Config\App; use Config\Services; use Tests\Support\Libraries\ConfigReader; -class HealthTest extends \CodeIgniter\Test\CIUnitTestCase +class HealthTest extends CIUnitTestCase { public function setUp(): void { @@ -28,7 +29,7 @@ class HealthTest extends \CodeIgniter\Test\CIUnitTestCase // Check the baseURL in .env if (is_file(HOMEPATH . '.env')) { $env = (bool) preg_grep( - '/^app\.baseURL = ./', + '~^app\.baseURL = .~', file(HOMEPATH . '.env'), ); } -- GitLab