From 9dcea7346778466b123e409c4e9d4b27894b1d9c Mon Sep 17 00:00:00 2001 From: Chris Kankiewicz Date: Tue, 18 Mar 2025 11:09:42 -0700 Subject: [PATCH] Static analysis fixes and code cleanup --- app/src/Bootstrap/AppManager.php | 7 +- app/src/Bootstrap/ExceptionManager.php | 3 +- app/src/Bootstrap/MiddlewareManager.php | 3 +- app/src/Bootstrap/RouteManager.php | 3 +- app/src/CallbackStream.php | 17 +- app/src/Config.php | 13 +- app/src/Controllers/DirectoryController.php | 12 +- app/src/Controllers/FileInfoController.php | 18 +- app/src/Controllers/IndexController.php | 2 - app/src/Controllers/SearchController.php | 2 - app/src/Controllers/ZipController.php | 32 +-- app/src/Exceptions/ErrorHandler.php | 2 - app/src/Exceptions/InvalidConfiguration.php | 8 +- app/src/Factories/CacheFactory.php | 9 +- app/src/Factories/FinderFactory.php | 4 +- app/src/Factories/TranslationFactory.php | 8 +- app/src/Factories/TwigFactory.php | 8 +- .../Middlewares/CacheControlMiddleware.php | 2 - app/src/Middlewares/PruneCacheMiddleware.php | 2 - .../Middlewares/RegisterGlobalsMiddleware.php | 2 - app/src/Middlewares/WhoopsMiddleware.php | 2 - app/src/SortMethods/Changed.php | 2 +- app/src/SortMethods/Type.php | 2 +- app/src/Support/Str.php | 2 + app/src/ViewFunctions/Breadcrumbs.php | 2 +- app/src/ViewFunctions/Config.php | 1 - app/src/ViewFunctions/Icon.php | 4 +- app/src/ViewFunctions/ModifiedTime.php | 3 +- app/src/ViewFunctions/ParentUrl.php | 2 +- app/src/ViewFunctions/SizeForHumans.php | 7 +- app/src/ViewFunctions/Translate.php | 1 - app/src/ViewFunctions/Url.php | 2 +- phpstan-baseline.neon | 219 +----------------- phpstan-ignores.neon | 2 + phpstan.neon.dist | 8 +- tests/Factories/CacheFactoryTest.php | 1 + .../Middlewares/PruneCacheMiddlewareTest.php | 2 + tests/Middlewares/WhoopsMiddlewareTest.php | 1 + 38 files changed, 88 insertions(+), 332 deletions(-) diff --git a/app/src/Bootstrap/AppManager.php b/app/src/Bootstrap/AppManager.php index f13b9e5..93ef609 100644 --- a/app/src/Bootstrap/AppManager.php +++ b/app/src/Bootstrap/AppManager.php @@ -10,12 +10,15 @@ use Slim\App; class AppManager { - /** Create a new AppManager object. */ public function __construct( private Container $container ) {} - /** Setup and configure the application. */ + /** + * Setup and configure the application. + * + * @return App + */ public function __invoke(): App { $app = Bridge::create($this->container); diff --git a/app/src/Bootstrap/ExceptionManager.php b/app/src/Bootstrap/ExceptionManager.php index ebcd8c4..a145e30 100644 --- a/app/src/Bootstrap/ExceptionManager.php +++ b/app/src/Bootstrap/ExceptionManager.php @@ -6,11 +6,12 @@ namespace App\Bootstrap; use App\Config; use App\Exceptions\ErrorHandler; +use DI\Container; use Slim\App; class ExceptionManager { - /** Create a new ExceptionManager object. */ + /** @param App $app */ public function __construct( private App $app, private Config $config diff --git a/app/src/Bootstrap/MiddlewareManager.php b/app/src/Bootstrap/MiddlewareManager.php index 5b57541..3b5e08b 100644 --- a/app/src/Bootstrap/MiddlewareManager.php +++ b/app/src/Bootstrap/MiddlewareManager.php @@ -5,11 +5,12 @@ declare(strict_types=1); namespace App\Bootstrap; use App\Config; +use DI\Container; use Slim\App; class MiddlewareManager { - /** Create a new MiddlwareManager object. */ + /** @param App $app */ public function __construct( private App $app, private Config $config diff --git a/app/src/Bootstrap/RouteManager.php b/app/src/Bootstrap/RouteManager.php index 009211d..455f6be 100644 --- a/app/src/Bootstrap/RouteManager.php +++ b/app/src/Bootstrap/RouteManager.php @@ -5,11 +5,12 @@ declare(strict_types=1); namespace App\Bootstrap; use App\Controllers; +use DI\Container; use Slim\App; class RouteManager { - /** Create a new RouteManager object. */ + /** @param App $app */ public function __construct( private App $app ) {} diff --git a/app/src/CallbackStream.php b/app/src/CallbackStream.php index 38a4c36..c5b1c40 100644 --- a/app/src/CallbackStream.php +++ b/app/src/CallbackStream.php @@ -95,11 +95,11 @@ class CallbackStream implements StreamInterface * @see http://www.php.net/manual/en/function.fseek.php * * @param int $offset Stream offset - * @param int $whence Specifies how the cursor position will be calculated - * based on the seek offset. Valid values are identical to the built-in - * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to - * offset bytes SEEK_CUR: Set position to current location plus offset - * SEEK_END: Set position to end-of-stream plus offset. + * @param int $whence Specifies how the cursor position will be calculated based on the seek offset. + * Valid values are identical to the built-in PHP $whence values for `fseek()`. + * - SEEK_SET: Set position equal to offset bytes + * - SEEK_CUR: Set position to current location plus offset + * - SEEK_END: Set position to end-of-stream plus offset. */ public function seek($offset, $whence = SEEK_SET): void {} @@ -154,7 +154,6 @@ class CallbackStream implements StreamInterface $this->called = true; - // Execute the callback call_user_func($this->callback); return ''; @@ -176,9 +175,9 @@ class CallbackStream implements StreamInterface * * @param string $key specific metadata to retrieve * - * @return array|mixed|null Returns an associative array if no key is - * provided. Returns a specific key value if a key is provided and the - * value is found, or null if the key is not found. + * @return array|mixed|null Returns an associative array if no key is provided. + * Returns a specific key value if a key is provided and + * the value is found, or null if the key is not found. */ public function getMetadata($key = null) { diff --git a/app/src/Config.php b/app/src/Config.php index 781a3f6..b99379b 100644 --- a/app/src/Config.php +++ b/app/src/Config.php @@ -9,23 +9,16 @@ use DI\NotFoundException; class Config { - /** Create a new Config object. */ public function __construct( private Container $container ) {} - /** - * Get the value of a configuration variable. - * - * @param mixed $default - * - * @return mixed - */ - public function get(string $key, $default = null) + /** Get the value of a configuration variable. */ + public function get(string $key, mixed $default = null): mixed { try { $value = $this->container->get($key); - } catch (NotFoundException $exception) { + } catch (NotFoundException) { return $default; } diff --git a/app/src/Controllers/DirectoryController.php b/app/src/Controllers/DirectoryController.php index fd7961c..e980cef 100644 --- a/app/src/Controllers/DirectoryController.php +++ b/app/src/Controllers/DirectoryController.php @@ -17,7 +17,6 @@ use Symfony\Contracts\Translation\TranslatorInterface; class DirectoryController { - /** Create a new IndexController object. */ public function __construct( private Container $container, private Config $config, @@ -26,7 +25,6 @@ class DirectoryController private TranslatorInterface $translator ) {} - /** Invoke the IndexController. */ public function __invoke(Request $request, Response $response): ResponseInterface { $relativePath = $request->getQueryParams()['dir'] ?? '.'; @@ -57,11 +55,11 @@ class DirectoryController $readmes = (clone $files)->name('/^README(?:\..+)?$/i'); - $readmes->filter(static function (SplFileInfo $file) { - return (bool) preg_match('/text\/.+/', (string) mime_content_type($file->getPathname())); - })->sort(static function (SplFileInfo $file1, SplFileInfo $file2) { - return $file1->getExtension() <=> $file2->getExtension(); - }); + $readmes->filter( + static fn (SplFileInfo $file): bool => (bool) preg_match('/text\/.+/', (string) mime_content_type($file->getPathname())) + )->sort( + static fn (SplFileInfo $file1, SplFileInfo $file2): int => $file1->getExtension() <=> $file2->getExtension() + ); if (! $readmes->hasResults()) { return null; diff --git a/app/src/Controllers/FileInfoController.php b/app/src/Controllers/FileInfoController.php index 2009dd8..55dfe0c 100644 --- a/app/src/Controllers/FileInfoController.php +++ b/app/src/Controllers/FileInfoController.php @@ -15,7 +15,6 @@ use Symfony\Contracts\Translation\TranslatorInterface; class FileInfoController { - /** Create a new FileInfoHandler object. */ public function __construct( private Container $container, private Config $config, @@ -23,7 +22,6 @@ class FileInfoController private TranslatorInterface $translator ) {} - /** Invoke the FileInfoHandler. */ public function __invoke(Request $request, Response $response): ResponseInterface { $path = $this->container->call('full_path', ['path' => $request->getQueryParams()['info']]); @@ -40,21 +38,23 @@ class FileInfoController $response->getBody()->write($this->cache->get( sprintf('file-info-%s', sha1((string) $file->getRealPath())), - function () use ($file): string { - return (string) json_encode(['hashes' => $this->calculateHashes($file)]); - } + fn (): string => (string) json_encode(['hashes' => $this->calculateHashes($file)], flags: JSON_THROW_ON_ERROR) )); return $response->withHeader('Content-Type', 'application/json'); } - /** Get an array of hashes for a file. */ + /** + * Get an array of hashes for a file. + * + * @return array{md5: string, sha1: string, sha256: string} + */ protected function calculateHashes(SplFileInfo $file): array { return [ - 'md5' => hash_file('md5', (string) $file->getRealPath()), - 'sha1' => hash_file('sha1', (string) $file->getRealPath()), - 'sha256' => hash_file('sha256', (string) $file->getRealPath()), + 'md5' => (string) hash_file('md5', (string) $file->getRealPath()), + 'sha1' => (string) hash_file('sha1', (string) $file->getRealPath()), + 'sha256' => (string) hash_file('sha256', (string) $file->getRealPath()), ]; } } diff --git a/app/src/Controllers/IndexController.php b/app/src/Controllers/IndexController.php index ea11acc..a2c5ccc 100644 --- a/app/src/Controllers/IndexController.php +++ b/app/src/Controllers/IndexController.php @@ -11,12 +11,10 @@ use Slim\Psr7\Response; class IndexController { - /** Create a new IndexController object. */ public function __construct( private Container $container ) {} - /** Invoke the IndexController. */ public function __invoke(Request $request, Response $response): ResponseInterface { $firstQueryParam = array_key_first($request->getQueryParams()); diff --git a/app/src/Controllers/SearchController.php b/app/src/Controllers/SearchController.php index 6ddcea5..d96a8ce 100644 --- a/app/src/Controllers/SearchController.php +++ b/app/src/Controllers/SearchController.php @@ -13,14 +13,12 @@ use Symfony\Contracts\Translation\TranslatorInterface; class SearchController { - /** Create a new SearchHandler object. */ public function __construct( private Finder $finder, private Twig $view, private TranslatorInterface $translator ) {} - /** Invoke the SearchHandler. */ public function __invoke(Request $request, Response $response): ResponseInterface { $search = $request->getQueryParams()['search']; diff --git a/app/src/Controllers/ZipController.php b/app/src/Controllers/ZipController.php index 59194aa..4c12e65 100644 --- a/app/src/Controllers/ZipController.php +++ b/app/src/Controllers/ZipController.php @@ -9,7 +9,6 @@ use App\Config; use App\Support\Str; use DateTime; use DI\Container; -use Exception; use Psr\Http\Message\ResponseInterface; use RuntimeException; use Slim\Psr7\Request; @@ -18,12 +17,12 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; use Symfony\Contracts\Translation\TranslatorInterface; use ZipStream\CompressionMethod; +use ZipStream\Exception as ZipStreamException; use ZipStream\OperationMode; use ZipStream\ZipStream; class ZipController { - /** Create a new ZipHandler object. */ public function __construct( private Container $container, private Config $config, @@ -31,11 +30,6 @@ class ZipController private TranslatorInterface $translator ) {} - /** Invoke the ZipHandler. - * @throws \ZipStream\Exception\FileNotFoundException - * @throws \ZipStream\Exception\FileNotReadableException - * @throws Exception - */ public function __invoke(Request $request, Response $response): ResponseInterface { $path = $this->container->call('full_path', ['path' => $request->getQueryParams()['zip']]); @@ -54,24 +48,25 @@ class ZipController $files = $this->finder->in($path)->files(); - $zip = $this->createZip($path, $files); + try { + $zip = $this->createZip($path, $files); + } catch (ZipStreamException) { + return $response->withStatus(500, $this->translator->trans('error.unexpected')); + } + $size = $zip->finish(); - $response = $this->augmentHeadersWithEstimatedSize($response, $size)->withBody( - new CallbackStream(static function () use ($zip) { + return $response->withHeader('Content-Length', (string) $size)->withBody( + new CallbackStream(static function () use ($zip): void { $zip->executeSimulation(); }) ); - - return $response; } /** * Create a zip stream from a directory. * - * @throws \ZipStream\Exception\FileNotFoundException - * @throws \ZipStream\Exception\FileNotReadableException - * @throws Exception + * @throws \ZipStream\Exception */ protected function createZip(string $path, Finder $files): ZipStream { @@ -102,11 +97,6 @@ class ZipController return $zip; } - protected function augmentHeadersWithEstimatedSize(Response $response, int $size): Response - { - return $response->withHeader('Content-Length', (string) $size); - } - /** Return the path to a file with the preceding root path stripped. */ protected function stripPath(SplFileInfo $file, string $path): string { @@ -118,7 +108,7 @@ class ZipController /** Generate the file name for a path. */ protected function generateFileName(string $path): string { - $filename = Str::explode($path, DIRECTORY_SEPARATOR)->last(); + $filename = (string) Str::explode($path, DIRECTORY_SEPARATOR)->last(); return $filename == '.' ? 'Home' : $filename; } diff --git a/app/src/Exceptions/ErrorHandler.php b/app/src/Exceptions/ErrorHandler.php index 28c4cb3..2dcff03 100644 --- a/app/src/Exceptions/ErrorHandler.php +++ b/app/src/Exceptions/ErrorHandler.php @@ -14,13 +14,11 @@ use Throwable; class ErrorHandler implements ErrorHandlerInterface { - /** Create a new ErrorHandler object. */ public function __construct( private Twig $view, private TranslatorInterface $translator ) {} - /** Invoke the ErrorHandler class. */ public function __invoke( ServerRequestInterface $request, Throwable $exception, diff --git a/app/src/Exceptions/InvalidConfiguration.php b/app/src/Exceptions/InvalidConfiguration.php index d828e8d..46aac88 100644 --- a/app/src/Exceptions/InvalidConfiguration.php +++ b/app/src/Exceptions/InvalidConfiguration.php @@ -8,12 +8,8 @@ use RuntimeException; final class InvalidConfiguration extends RuntimeException { - /** - * Create an exception from a configuration option and value. - * - * @param mixed $value - */ - public static function fromConfig(string $option, $value): self + /** Create an exception from a configuration option and value. */ + public static function fromConfig(string $option, mixed $value): self { return new static( sprintf("Unknown value %s for configuration option '%s'", var_export($value, true), $option) diff --git a/app/src/Factories/CacheFactory.php b/app/src/Factories/CacheFactory.php index 66060bc..cfb7d7b 100644 --- a/app/src/Factories/CacheFactory.php +++ b/app/src/Factories/CacheFactory.php @@ -19,19 +19,14 @@ use Symfony\Contracts\Cache\CacheInterface; class CacheFactory { - /** @const Namespace for external cache drivers */ - protected const NAMESPACE_EXTERNAL = 'directory_lister'; + private const NAMESPACE_EXTERNAL = 'directory_lister'; + private const NAMESPACE_INTERNAL = 'app'; - /** @const Namespace for internal cache drivers */ - protected const NAMESPACE_INTERNAL = 'app'; - - /** Create a new CacheFactory object. */ public function __construct( private Container $container, private Config $config ) {} - /** Initialize and return a CacheInterface. */ public function __invoke(): CacheInterface { return match ($this->config->get('cache_driver')) { diff --git a/app/src/Factories/FinderFactory.php b/app/src/Factories/FinderFactory.php index b8d7db6..0797032 100644 --- a/app/src/Factories/FinderFactory.php +++ b/app/src/Factories/FinderFactory.php @@ -16,17 +16,15 @@ use Symfony\Component\Finder\SplFileInfo; class FinderFactory { - /** @var ?Pattern Hidden files pattern cache */ + /** Hidden files pattern cache */ private ?Pattern $pattern = null; - /** Create a new FinderFactory object. */ public function __construct( private Container $container, private Config $config, private HiddenFiles $hiddenFiles ) {} - /** Initialize and return the Finder component. */ public function __invoke(): Finder { $finder = Finder::create()->followLinks(); diff --git a/app/src/Factories/TranslationFactory.php b/app/src/Factories/TranslationFactory.php index ad42878..680d992 100644 --- a/app/src/Factories/TranslationFactory.php +++ b/app/src/Factories/TranslationFactory.php @@ -15,13 +15,11 @@ use Symfony\Contracts\Translation\TranslatorInterface; class TranslationFactory { - /** Create a new TranslationFactory object. */ public function __construct( private Config $config, private CacheInterface $cache ) {} - /** Initialize and return the translation component. */ public function __invoke(): TranslatorInterface { if (! in_array( @@ -43,7 +41,11 @@ class TranslationFactory return $translator; } - /** Get an array of available translation languages. */ + /** + * Get an array of available translation languages. + * + * @return list + */ protected function translations(): array { return $this->cache->get('translations', function (): array { diff --git a/app/src/Factories/TwigFactory.php b/app/src/Factories/TwigFactory.php index 97010ba..edc01b3 100644 --- a/app/src/Factories/TwigFactory.php +++ b/app/src/Factories/TwigFactory.php @@ -14,18 +14,16 @@ use Twig\TwigFunction; class TwigFactory { - /** Create a new TwigFactory object. */ public function __construct( private Config $config, private CallableResolver $callableResolver ) {} - /** Initialize and return the Twig component. */ public function __invoke(): Twig { - $twig = new Twig(new FilesystemLoader( - $this->config->get('views_path') - ), ['cache' => $this->config->get('view_cache')]); + $twig = new Twig(new FilesystemLoader($this->config->get('views_path')), [ + 'cache' => $this->config->get('view_cache'), + ]); /** @var CoreExtension $core */ $core = $twig->getEnvironment()->getExtension(CoreExtension::class); diff --git a/app/src/Middlewares/CacheControlMiddleware.php b/app/src/Middlewares/CacheControlMiddleware.php index fb3b3a2..ad0b6a8 100644 --- a/app/src/Middlewares/CacheControlMiddleware.php +++ b/app/src/Middlewares/CacheControlMiddleware.php @@ -11,12 +11,10 @@ use Psr\Http\Server\RequestHandlerInterface as RequestHandler; class CacheControlMiddleware { - /** Create a new CacheControlMiddleware object. */ public function __construct( private Config $config ) {} - /** Invoke the CacheControlMiddleware class. */ public function __invoke(Request $request, RequestHandler $handler): ResponseInterface { $response = $handler->handle($request); diff --git a/app/src/Middlewares/PruneCacheMiddleware.php b/app/src/Middlewares/PruneCacheMiddleware.php index e7712bd..0f30519 100644 --- a/app/src/Middlewares/PruneCacheMiddleware.php +++ b/app/src/Middlewares/PruneCacheMiddleware.php @@ -13,13 +13,11 @@ use Symfony\Contracts\Cache\CacheInterface; class PruneCacheMiddleware { - /** Create a new CachePruneMiddleware object. */ public function __construct( private Config $config, private CacheInterface $cache ) {} - /** Invoke the CachePruneMiddleware class. */ public function __invoke(Request $request, RequestHandler $handler): ResponseInterface { $response = $handler->handle($request); diff --git a/app/src/Middlewares/RegisterGlobalsMiddleware.php b/app/src/Middlewares/RegisterGlobalsMiddleware.php index b9a2c4c..d8a9cd1 100644 --- a/app/src/Middlewares/RegisterGlobalsMiddleware.php +++ b/app/src/Middlewares/RegisterGlobalsMiddleware.php @@ -11,14 +11,12 @@ use Slim\Views\Twig; class RegisterGlobalsMiddleware { - /** Array of valid theme strings. */ private const VALID_THEMES = ['dark', 'light']; public function __construct( private Twig $view ) {} - /** Invoke the RegisterGlobalsMiddleware class. */ public function __invoke(Request $request, RequestHandler $handler): ResponseInterface { $this->view->getEnvironment()->addGlobal('theme', $this->getThemeFromRequest($request)); diff --git a/app/src/Middlewares/WhoopsMiddleware.php b/app/src/Middlewares/WhoopsMiddleware.php index 7239446..7184c41 100644 --- a/app/src/Middlewares/WhoopsMiddleware.php +++ b/app/src/Middlewares/WhoopsMiddleware.php @@ -13,14 +13,12 @@ use Whoops\RunInterface; class WhoopsMiddleware { - /** Create a new WhoopseMiddleware object. */ public function __construct( private RunInterface $whoops, private PrettyPageHandler $pageHandler, private JsonResponseHandler $jsonHandler ) {} - /** Invoke the WhoopseMiddleware class. */ public function __invoke(Request $request, RequestHandler $handler): ResponseInterface { $this->pageHandler->setPageTitle( diff --git a/app/src/SortMethods/Changed.php b/app/src/SortMethods/Changed.php index 6a87879..54cea24 100644 --- a/app/src/SortMethods/Changed.php +++ b/app/src/SortMethods/Changed.php @@ -8,7 +8,7 @@ use Symfony\Component\Finder\Finder; class Changed extends SortMethod { - /** Sory by file changed time. */ + /** Sort by file changed time. */ public function __invoke(Finder $finder): void { $finder->sortByChangedTime(); diff --git a/app/src/SortMethods/Type.php b/app/src/SortMethods/Type.php index a31513c..dc7a754 100644 --- a/app/src/SortMethods/Type.php +++ b/app/src/SortMethods/Type.php @@ -8,7 +8,7 @@ use Symfony\Component\Finder\Finder; class Type extends SortMethod { - /** Sory by file type. */ + /** Sort by file type. */ public function __invoke(Finder $finder): void { $finder->sortByType(); diff --git a/app/src/Support/Str.php b/app/src/Support/Str.php index 99c72c7..9480f19 100644 --- a/app/src/Support/Str.php +++ b/app/src/Support/Str.php @@ -11,6 +11,8 @@ class Str /** * Explode a string by a string into a collection. * + * @param non-empty-string $delimiter + * * @return Collection */ public static function explode(string $string, string $delimiter): Collection diff --git a/app/src/ViewFunctions/Breadcrumbs.php b/app/src/ViewFunctions/Breadcrumbs.php index 290e7af..e68aa63 100644 --- a/app/src/ViewFunctions/Breadcrumbs.php +++ b/app/src/ViewFunctions/Breadcrumbs.php @@ -12,7 +12,7 @@ class Breadcrumbs extends ViewFunction { protected string $name = 'breadcrumbs'; - /** Create a new Breadcrumbs object. */ + /** @param non-empty-string $directorySeparator */ public function __construct( private Config $config, private string $directorySeparator = DIRECTORY_SEPARATOR diff --git a/app/src/ViewFunctions/Config.php b/app/src/ViewFunctions/Config.php index ba856cd..388cc28 100644 --- a/app/src/ViewFunctions/Config.php +++ b/app/src/ViewFunctions/Config.php @@ -10,7 +10,6 @@ class Config extends ViewFunction { protected string $name = 'config'; - /** Create a new Config object. */ public function __construct( private AppConfig $config ) {} diff --git a/app/src/ViewFunctions/Icon.php b/app/src/ViewFunctions/Icon.php index ae45f95..2917e82 100644 --- a/app/src/ViewFunctions/Icon.php +++ b/app/src/ViewFunctions/Icon.php @@ -11,7 +11,6 @@ class Icon extends ViewFunction { protected string $name = 'icon'; - /** Create a new Config object. */ public function __construct( private Config $config ) {} @@ -21,8 +20,7 @@ class Icon extends ViewFunction { $icons = $this->config->get('icons'); - $icon = $file->isDir() ? 'fas fa-folder' - : $icons[strtolower($file->getExtension())] ?? 'fas fa-file'; + $icon = $file->isDir() ? 'fas fa-folder' : $icons[strtolower($file->getExtension())] ?? 'fas fa-file'; return ""; } diff --git a/app/src/ViewFunctions/ModifiedTime.php b/app/src/ViewFunctions/ModifiedTime.php index d160eac..420636e 100644 --- a/app/src/ViewFunctions/ModifiedTime.php +++ b/app/src/ViewFunctions/ModifiedTime.php @@ -20,8 +20,9 @@ class ModifiedTime extends ViewFunction public function __invoke(SplFileInfo $file): string { try { + /** @throws RuntimeException */ $modifiedTime = $file->getMTime(); - } catch (RuntimeException $exception) { + } catch (RuntimeException) { $modifiedTime = lstat($file->getPathname())['mtime']; } diff --git a/app/src/ViewFunctions/ParentUrl.php b/app/src/ViewFunctions/ParentUrl.php index 9296d31..d8dde19 100644 --- a/app/src/ViewFunctions/ParentUrl.php +++ b/app/src/ViewFunctions/ParentUrl.php @@ -10,7 +10,7 @@ class ParentUrl extends ViewFunction { protected string $name = 'parent_url'; - /** Create a new ParentUrl object. */ + /** @param non-empty-string $directorySeparator */ public function __construct( private string $directorySeparator = DIRECTORY_SEPARATOR ) {} diff --git a/app/src/ViewFunctions/SizeForHumans.php b/app/src/ViewFunctions/SizeForHumans.php index 96085a4..d260090 100644 --- a/app/src/ViewFunctions/SizeForHumans.php +++ b/app/src/ViewFunctions/SizeForHumans.php @@ -9,6 +9,8 @@ use Symfony\Component\Finder\SplFileInfo; class SizeForHumans extends ViewFunction { + private const array UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + protected string $name = 'size_for_humans'; /** Get the human readable file size from a file object. */ @@ -16,13 +18,12 @@ class SizeForHumans extends ViewFunction { try { $fileSize = $file->getSize(); - } catch (RuntimeException $exception) { + } catch (RuntimeException) { return '0B'; } - $sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; $factor = (int) floor((strlen((string) $fileSize) - 1) / 3); - return sprintf('%.2f%s', $fileSize / pow(1024, $factor), $sizes[$factor]); + return sprintf('%.2f%s', $fileSize / pow(1024, $factor), self::UNITS[$factor]); } } diff --git a/app/src/ViewFunctions/Translate.php b/app/src/ViewFunctions/Translate.php index e720658..81241ec 100644 --- a/app/src/ViewFunctions/Translate.php +++ b/app/src/ViewFunctions/Translate.php @@ -10,7 +10,6 @@ class Translate extends ViewFunction { protected string $name = 'translate'; - /** Create a new Translate object. */ public function __construct( private TranslatorInterface $translator ) {} diff --git a/app/src/ViewFunctions/Url.php b/app/src/ViewFunctions/Url.php index b9bfa88..eb220af 100644 --- a/app/src/ViewFunctions/Url.php +++ b/app/src/ViewFunctions/Url.php @@ -11,7 +11,7 @@ class Url extends ViewFunction { protected string $name = 'url'; - /** Create a new Url object. */ + /** @param non-empty-string $directorySeparator */ public function __construct( private Config $config, private string $directorySeparator = DIRECTORY_SEPARATOR diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 235d21a..9e42d71 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,71 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Method App\\Bootstrap\\AppManager\:\:__invoke\(\) return type with generic class Slim\\App does not specify its types\: TContainerInterface$#' - identifier: missingType.generics - count: 1 - path: app/src/Bootstrap/AppManager.php - - - - message: '#^Method App\\Bootstrap\\ExceptionManager\:\:__construct\(\) has parameter \$app with generic class Slim\\App but does not specify its types\: TContainerInterface$#' - identifier: missingType.generics - count: 1 - path: app/src/Bootstrap/ExceptionManager.php - - - - message: '#^Method App\\Bootstrap\\MiddlewareManager\:\:__construct\(\) has parameter \$app with generic class Slim\\App but does not specify its types\: TContainerInterface$#' - identifier: missingType.generics - count: 1 - path: app/src/Bootstrap/MiddlewareManager.php - - - - message: '#^Method App\\Bootstrap\\RouteManager\:\:__construct\(\) has parameter \$app with generic class Slim\\App but does not specify its types\: TContainerInterface$#' - identifier: missingType.generics - count: 1 - path: app/src/Bootstrap/RouteManager.php - - - - message: '#^Method App\\Controllers\\DirectoryController\:\:__invoke\(\) throws checked exception Twig\\Error\\LoaderError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/DirectoryController.php - - - - message: '#^Method App\\Controllers\\DirectoryController\:\:__invoke\(\) throws checked exception Twig\\Error\\RuntimeError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/DirectoryController.php - - - - message: '#^Method App\\Controllers\\DirectoryController\:\:__invoke\(\) throws checked exception Twig\\Error\\SyntaxError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/DirectoryController.php - - - - message: '#^Method App\\Controllers\\FileInfoController\:\:calculateHashes\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: app/src/Controllers/FileInfoController.php - - - - message: '#^Method App\\Controllers\\SearchController\:\:__invoke\(\) throws checked exception Twig\\Error\\LoaderError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/SearchController.php - - - - message: '#^Method App\\Controllers\\SearchController\:\:__invoke\(\) throws checked exception Twig\\Error\\RuntimeError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/SearchController.php - - - - message: '#^Method App\\Controllers\\SearchController\:\:__invoke\(\) throws checked exception Twig\\Error\\SyntaxError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 2 - path: app/src/Controllers/SearchController.php - - message: '#^Cannot access offset ''mtime'' on array\{0\: int, 1\: int, 2\: int, 3\: int, 4\: int, 5\: int, 6\: int, 7\: int, \.\.\.\}\|false\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -73,35 +7,11 @@ parameters: path: app/src/Controllers/ZipController.php - - message: '#^Method App\\Controllers\\ZipController\:\:generateFileName\(\) should return string but returns string\|null\.$#' - identifier: return.type + message: '#^Method App\\Controllers\\ZipController\:\:createZip\(\) throws checked exception DateMalformedStringException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: app/src/Controllers/ZipController.php - - - message: '#^Method App\\Exceptions\\ErrorHandler\:\:__invoke\(\) throws checked exception Twig\\Error\\LoaderError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: app/src/Exceptions/ErrorHandler.php - - - - message: '#^Method App\\Exceptions\\ErrorHandler\:\:__invoke\(\) throws checked exception Twig\\Error\\RuntimeError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: app/src/Exceptions/ErrorHandler.php - - - - message: '#^Method App\\Exceptions\\ErrorHandler\:\:__invoke\(\) throws checked exception Twig\\Error\\SyntaxError but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: app/src/Exceptions/ErrorHandler.php - - - - message: '#^Method App\\Factories\\TranslationFactory\:\:translations\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: app/src/Factories/TranslationFactory.php - - message: '#^Method App\\Factories\\TwigFactory\:\:__invoke\(\) throws checked exception ReflectionException but it''s missing from the PHPDoc @throws tag\.$#' identifier: missingType.checkedException @@ -109,44 +19,8 @@ parameters: path: app/src/Factories/TwigFactory.php - - message: '#^Method App\\Middlewares\\PruneCacheMiddleware\:\:winsLottery\(\) throws checked exception Random\\RandomException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: app/src/Middlewares/PruneCacheMiddleware.php - - - - message: '#^Parameter \#1 \$separator of function explode expects non\-empty\-string, string given\.$#' - identifier: argument.type - count: 1 - path: app/src/Support/Str.php - - - - message: '#^Unable to resolve the template type TMakeKey in call to method static method Tightenco\\Collect\\Support\\Collection\<\(int\|string\),mixed\>\:\:make\(\)$#' - identifier: argument.templateType - count: 1 - path: app/src/ViewFunctions/Asset.php - - - - message: '#^Unable to resolve the template type TMakeValue in call to method static method Tightenco\\Collect\\Support\\Collection\<\(int\|string\),mixed\>\:\:make\(\)$#' - identifier: argument.templateType - count: 1 - path: app/src/ViewFunctions/Asset.php - - - - message: '#^Method App\\ViewFunctions\\Breadcrumbs\:\:__invoke\(\) should return Tightenco\\Collect\\Support\\Collection\ but returns Tightenco\\Collect\\Support\\Collection\<\*NEVER\*, non\-falsy\-string\>\.$#' - identifier: return.type - count: 1 - path: app/src/ViewFunctions/Breadcrumbs.php - - - - message: '#^Parameter \#1 \$separator of function explode expects non\-empty\-string, string given\.$#' - identifier: argument.type - count: 1 - path: app/src/ViewFunctions/Breadcrumbs.php - - - - message: '#^Dead catch \- RuntimeException is never thrown in the try block\.$#' - identifier: catch.neverThrown + message: '#^Cannot access offset ''mtime'' on array\{0\: int, 1\: int, 2\: int, 3\: int, 4\: int, 5\: int, 6\: int, 7\: int, \.\.\.\}\|false\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: app/src/ViewFunctions/ModifiedTime.php @@ -156,93 +30,8 @@ parameters: count: 1 path: app/src/ViewFunctions/SizeForHumans.php - - - message: '#^Method Tests\\Controllers\\DirectoryControllerTest\:\:configOptions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Controllers/DirectoryControllerTest.php - - message: '#^Parameter \#1 \$finfo of function finfo_buffer expects finfo, finfo\|false given\.$#' identifier: argument.type count: 1 path: tests/Exceptions/ErrorHandlerTest.php - - - - message: '#^Method Tests\\Factories\\CacheFactoryTest\:\:cacheAdapters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Factories/CacheFactoryTest.php - - - - message: '#^Parameter \#1 \$expected of method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) expects class\-string\, string given\.$#' - identifier: argument.type - count: 1 - path: tests/Factories/CacheFactoryTest.php - - - - message: '#^Method Tests\\Factories\\FinderFactoryTest\:\:getFilesArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Factories/FinderFactoryTest.php - - - - message: '#^Cannot call method getCallable\(\) on Twig\\TwigFunction\|null\.$#' - identifier: method.nonObject - count: 11 - path: tests/Factories/TwigFactoryTest.php - - - - message: '#^Method Tests\\HiddenFilesTest\:\:hiddenFilesProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/HiddenFilesTest.php - - - - message: '#^Method Tests\\HiddenFilesTest\:\:test_it_creates_a_collection_of_hidden_files\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/HiddenFilesTest.php - - - - message: '#^Method Tests\\HiddenFilesTest\:\:test_it_creates_a_collection_of_hidden_files\(\) has parameter \$hiddenFilesArray with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/HiddenFilesTest.php - - - - message: '#^Method Tests\\Middlewares\\PruneCacheMiddlewareTest\:\:nonPruneableCacheAdapters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - - - - message: '#^Method Tests\\Middlewares\\PruneCacheMiddlewareTest\:\:pruneableCacheAdapters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - - - - message: '#^Parameter \#1 \$className of method PHPUnit\\Framework\\TestCase\:\:getMockBuilder\(\) expects class\-string\, string given\.$#' - identifier: argument.type - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - - - - message: '#^Parameter \#1 \$originalClassName of method PHPUnit\\Framework\\TestCase\:\:createMock\(\) expects class\-string\, string given\.$#' - identifier: argument.type - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - - - - message: '#^Unable to resolve the template type RealInstanceType in call to method PHPUnit\\Framework\\TestCase\:\:createMock\(\)$#' - identifier: argument.templateType - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - - - - message: '#^Unable to resolve the template type RealInstanceType in call to method PHPUnit\\Framework\\TestCase\:\:getMockBuilder\(\)$#' - identifier: argument.templateType - count: 1 - path: tests/Middlewares/PruneCacheMiddlewareTest.php - diff --git a/phpstan-ignores.neon b/phpstan-ignores.neon index 6479486..10a856a 100644 --- a/phpstan-ignores.neon +++ b/phpstan-ignores.neon @@ -4,3 +4,5 @@ parameters: path: tests/* - message: "#^Method .+ has parameter .+ with no value type specified in iterable type array\\.$#" path: tests/* + - message: "#^Method .+ return type has no value type specified in iterable type array\\.$#" + path: tests/* diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f48bbe2..5193475 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -16,8 +16,6 @@ parameters: checkFunctionNameCase: true - reportUnmatchedIgnoredErrors: false - exceptions: implicitThrows: false @@ -31,11 +29,13 @@ parameters: - 'Invoker\Exception\NotCallableException' - 'JsonException' - 'LogicException' - - 'RuntimeException' + - 'PHPUnit\Framework\Exception' - 'Psr\Cache\InvalidArgumentException' + - 'Random\RandomException' + - 'RuntimeException' - 'SebastianBergmann\RecursionContext\InvalidArgumentException' - 'Symfony\Component\Cache\Exception\CacheException' - - 'PHPUnit\Framework\Exception' + - 'Twig\Error\Error' includes: - phpstan-baseline.neon diff --git a/tests/Factories/CacheFactoryTest.php b/tests/Factories/CacheFactoryTest.php index f000a0d..e77b9ad 100644 --- a/tests/Factories/CacheFactoryTest.php +++ b/tests/Factories/CacheFactoryTest.php @@ -28,6 +28,7 @@ class CacheFactoryTest extends TestCase ]; } + /** @param class-string $adapter */ #[Test, DataProvider('cacheAdapters')] public function it_can_compose_an_adapter(string $config, string $adapter, bool $available = true): void { diff --git a/tests/Middlewares/PruneCacheMiddlewareTest.php b/tests/Middlewares/PruneCacheMiddlewareTest.php index 10a4a57..b90d03b 100644 --- a/tests/Middlewares/PruneCacheMiddlewareTest.php +++ b/tests/Middlewares/PruneCacheMiddlewareTest.php @@ -43,6 +43,7 @@ class PruneCacheMiddlewareTest extends TestCase ]; } + /** @param class-string $cacheAdapter */ #[Test, DataProvider('pruneableCacheAdapters')] public function it_prunes_the_cache_whe_using_a_pruneable_adapter_and_winning_the_lottery(string $cacheAdapter): void { @@ -56,6 +57,7 @@ class PruneCacheMiddlewareTest extends TestCase ); } + /** @param class-string $cacheAdapter */ #[Test, DataProvider('nonPruneableCacheAdapters')] public function it_does_not_prune_the_cache_when_using_a_non_prunable_adapter(string $cacheAdapter): void { diff --git a/tests/Middlewares/WhoopsMiddlewareTest.php b/tests/Middlewares/WhoopsMiddlewareTest.php index ccefee9..ab476e1 100644 --- a/tests/Middlewares/WhoopsMiddlewareTest.php +++ b/tests/Middlewares/WhoopsMiddlewareTest.php @@ -62,6 +62,7 @@ class WhoopsMiddlewareTest extends TestCase fn (Handler $parameter) => match ($matcher->numberOfInvocations()) { 1 => $this->assertSame($pageHandler, $parameter), 2 => $this->assertSame($jsonHandler, $parameter), + default => $this->fail('Unexpected invocation') } );