From 3197873d0ff7c0b66bd08de139eeaad1cb2a94dc Mon Sep 17 00:00:00 2001 From: Michel Roux Date: Thu, 6 Mar 2025 18:36:38 +0100 Subject: [PATCH] fix: :white_check_mark: fix phpstan validation on responses --- composer.json | 1 + composer.lock | 62 ++++++++++++++++++++++++--- lib/Controller/EpisodesController.php | 16 ++++++- lib/Controller/OpmlController.php | 11 ++++- lib/Controller/PageController.php | 17 ++++++-- lib/Controller/PodcastController.php | 7 ++- lib/Controller/SearchController.php | 8 +++- lib/Controller/ToplistController.php | 13 +++++- phpstan.neon | 4 +- 9 files changed, 119 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index fdde39b..23e2040 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "nextcloud/ocp": "^31.0.0", "nextcloud/rector": "^0.3.1", "phpstan/phpstan": "~1.12.20", + "phpstan/phpstan-deprecation-rules": "~1.2.1", "rector/rector": "~1.2.10", "roave/security-advisories": "dev-latest" }, diff --git a/composer.lock b/composer.lock index 5cba5fd..df9b2c8 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": "0142d63c8ac24699581a19cd69148107", + "content-hash": "600a8f571b8f0aba7eacf029c105560d", "packages": [], "packages-dev": [ { @@ -315,6 +315,53 @@ ], "time": "2025-03-05T13:37:43+00:00" }, + { + "name": "phpstan/phpstan-deprecation-rules", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", + "reference": "f94d246cc143ec5a23da868f8f7e1393b50eaa82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/f94d246cc143ec5a23da868f8f7e1393b50eaa82", + "reference": "f94d246cc143ec5a23da868f8f7e1393b50eaa82", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.12" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.2.1" + }, + "time": "2024-09-11T15:52:35+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -581,12 +628,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "e7cacb4409da236548159072e5eff2c30cd3bf9c" + "reference": "93d6f477df354be282736a37f732e39382ff9f95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e7cacb4409da236548159072e5eff2c30cd3bf9c", - "reference": "e7cacb4409da236548159072e5eff2c30cd3bf9c", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/93d6f477df354be282736a37f732e39382ff9f95", + "reference": "93d6f477df354be282736a37f732e39382ff9f95", "shasum": "" }, "conflict": { @@ -914,7 +961,7 @@ "lara-zeus/artemis": ">=1,<=1.0.6", "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<6.20.45|>=7,<7.30.7|>=8,<8.83.28|>=9,<9.52.17|>=10,<10.48.23|>=11,<11.31", + "laravel/framework": "<11.44.1|>=12,<12.1.1", "laravel/laravel": ">=5.4,<5.4.22", "laravel/pulse": "<1.3.1", "laravel/reverb": "<1.4", @@ -934,6 +981,7 @@ "limesurvey/limesurvey": "<6.5.12", "livehelperchat/livehelperchat": "<=3.91", "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2", + "livewire/volt": "<1.7", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", "luyadev/yii-helpers": "<1.2.1", @@ -1123,7 +1171,7 @@ "rap2hpoutre/laravel-log-viewer": "<0.13", "react/http": ">=0.7,<1.9", "really-simple-plugins/complianz-gdpr": "<6.4.2", - "redaxo/source": "<=5.18.1", + "redaxo/source": "<5.18.3", "remdex/livehelperchat": "<4.29", "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", @@ -1449,7 +1497,7 @@ "type": "tidelift" } ], - "time": "2025-03-03T20:05:19+00:00" + "time": "2025-03-05T20:05:33+00:00" } ], "aliases": [], diff --git a/lib/Controller/EpisodesController.php b/lib/Controller/EpisodesController.php index b150211..05a0841 100644 --- a/lib/Controller/EpisodesController.php +++ b/lib/Controller/EpisodesController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\RePod\Controller; +use OCA\GPodderSync\Core\EpisodeAction\EpisodeAction; use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository; use OCA\RePod\AppInfo\Application; use OCA\RePod\Core\EpisodeAction\EpisodeActionExtraData; @@ -15,9 +16,13 @@ use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; use OCP\Http\Client\IClientService; use OCP\IRequest; +/** + * @phpstan-import-type EpisodeActionType from EpisodeAction + */ class EpisodesController extends Controller { public function __construct( @@ -30,10 +35,13 @@ class EpisodesController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return JSONResponse, array{}> + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/episodes/list')] - public function list(string $url): JSONResponse { + public function list(string $url): Response { $client = $this->clientService->newClient(); $feed = $client->get($url); $episodes = $this->episodeActionReader->parseRssXml((string) $feed->getBody()); @@ -46,16 +54,20 @@ class EpisodesController extends Controller return new JSONResponse($episodes, $returnStatusCode); } + /** + * @return JSONResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/episodes/action')] - public function action(string $url): JSONResponse { + public function action(string $url): Response { $action = $this->episodeActionRepository->findByEpisodeUrl($url, $this->userService->getUserUID()); if ($action) { return new JSONResponse($action->toArray()); } + /** @phpstan-ignore-next-line */ return new JSONResponse([], Http::STATUS_NOT_FOUND); } } diff --git a/lib/Controller/OpmlController.php b/lib/Controller/OpmlController.php index cbefc5f..234e52a 100644 --- a/lib/Controller/OpmlController.php +++ b/lib/Controller/OpmlController.php @@ -10,6 +10,7 @@ use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangeSaver; use OCA\RePod\AppInfo\Application; use OCA\RePod\Service\UserService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; @@ -31,10 +32,13 @@ class OpmlController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return DataDownloadResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/opml/export')] - public function export(): DataDownloadResponse { + public function export(): Response { // https://github.com/AntennaPod/AntennaPod/blob/master/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java $xml = new \SimpleXMLElement('', namespaceOrPrefix: 'http://xmlpull.org/v1/doc/features.html#indent-output'); $xml->addAttribute('version', '2.0'); @@ -81,9 +85,12 @@ class OpmlController extends Controller } } - return new DataDownloadResponse((string) $xml->asXML(), 'repod-'.$dateCreated->getTimestamp().'.opml', ' application/xml'); + return new DataDownloadResponse((string) $xml->asXML(), 'repod-'.$dateCreated->getTimestamp().'.opml', 'application/xml'); } + /** + * @return Response + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'POST', url: '/opml/import')] diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 88db662..4654b38 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -6,10 +6,12 @@ namespace OCA\RePod\Controller; use OCA\RePod\AppInfo\Application; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\IRequest; @@ -21,10 +23,13 @@ class PageController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return TemplateResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/')] - public function index(): TemplateResponse { + public function index(): Response { $csp = new ContentSecurityPolicy(); $csp->addAllowedImageDomain('*'); $csp->addAllowedMediaDomain('*'); @@ -35,17 +40,23 @@ class PageController extends Controller return $response; } + /** + * @return TemplateResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/discover')] - public function discover(): TemplateResponse { + public function discover(): Response { return $this->index(); } + /** + * @return TemplateResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/feed/{path}', requirements: ['path' => '.+'])] - public function feed(): TemplateResponse { + public function feed(): Response { return $this->index(); } } diff --git a/lib/Controller/PodcastController.php b/lib/Controller/PodcastController.php index 5fcfd68..2450c2d 100644 --- a/lib/Controller/PodcastController.php +++ b/lib/Controller/PodcastController.php @@ -13,6 +13,7 @@ use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; use OCP\Http\Client\IClientService; use OCP\ICacheFactory; use OCP\IRequest; @@ -28,10 +29,13 @@ class PodcastController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return JSONResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/podcast')] - public function index(string $url): JSONResponse { + public function index(string $url): Response { $podcast = null; if ($this->cacheFactory->isLocalCacheAvailable()) { @@ -59,6 +63,7 @@ class PodcastController extends Controller /** @var Http::STATUS_* $returnStatusCode */ $returnStatusCode = $feed->getStatusCode(); + /** @phpstan-ignore-next-line */ return new JSONResponse($podcast, $returnStatusCode); } } diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php index b42e131..36b2c52 100644 --- a/lib/Controller/SearchController.php +++ b/lib/Controller/SearchController.php @@ -4,13 +4,16 @@ declare(strict_types=1); namespace OCA\RePod\Controller; +use OCA\GPodderSync\Core\PodcastData\PodcastData; use OCA\RePod\AppInfo\Application; use OCA\RePod\Service\MultiPodService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; use OCP\IRequest; class SearchController extends Controller @@ -22,10 +25,13 @@ class SearchController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return JSONResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/search')] - public function index(string $q): JSONResponse { + public function index(string $q): Response { return new JSONResponse($this->multiPodService->search($q)); } } diff --git a/lib/Controller/ToplistController.php b/lib/Controller/ToplistController.php index 6e9d073..b8944f4 100644 --- a/lib/Controller/ToplistController.php +++ b/lib/Controller/ToplistController.php @@ -4,13 +4,16 @@ declare(strict_types=1); namespace OCA\RePod\Controller; +use OCA\GPodderSync\Core\PodcastData\PodcastData; use OCA\RePod\AppInfo\Application; use OCA\RePod\Service\FyydService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; use OCP\IRequest; class ToplistController extends Controller @@ -22,17 +25,23 @@ class ToplistController extends Controller parent::__construct(Application::APP_ID, $request); } + /** + * @return JSONResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/toplist/hot')] - public function hot(): JSONResponse { + public function hot(): Response { return new JSONResponse($this->fyydService->hot()); } + /** + * @return JSONResponse + */ #[NoAdminRequired] #[NoCSRFRequired] #[FrontpageRoute(verb: 'GET', url: '/toplist/new')] - public function new(): JSONResponse { + public function new(): Response { return new JSONResponse($this->fyydService->latest()); } } diff --git a/phpstan.neon b/phpstan.neon index 550c70d..7294319 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,5 +2,5 @@ parameters: level: 9 paths: - lib - ignoreErrors: - - '#Method [a-zA-Z0-9:\(\)\\_]+ return type with generic class OCP\\AppFramework\\Http\\[a-zA-Z]+ does not specify its types#' +includes: + - vendor/phpstan/phpstan-deprecation-rules/rules.neon