diff --git a/composer.json b/composer.json index 479508072..51c084e5c 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,9 @@ "cs:check": "php-cs-fixer fix --dry-run --diff", "cs:fix": "php-cs-fixer fix", "test:unit": "phpunit -c tests/phpunit.xml", - "psalm": "psalm --no-cache --threads=$(nproc)" + "psalm": "psalm --no-cache --threads=$(nproc)", + "rector:check": "rector --dry-run --clear-cache", + "rector:fix": "rector && php-cs-fixer fix" }, "autoload-dev": { "psr-4": { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 02d409f1e..927383c8b 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -30,9 +30,7 @@ public function register(IRegistrationContext $context): void { if (defined('OC_CONSOLE') && \OC_CONSOLE) { $context->registerEventListener(BeforeMessageLoggedEvent::class, LogListener::class); } - $context->registerService(Formatter::class, function () { - return new Formatter(\OC::$SERVERROOT); - }); + $context->registerService(Formatter::class, fn () => new Formatter(\OC::$SERVERROOT)); $context->registerSetupCheck(LogErrors::class); } diff --git a/lib/Command/Tail.php b/lib/Command/Tail.php index 08d85443c..f59c0c840 100644 --- a/lib/Command/Tail.php +++ b/lib/Command/Tail.php @@ -24,13 +24,11 @@ class Tail extends Base { public const LEVELS = ['Debug', 'Info', 'Warning', 'Error', 'Fatal']; - private $formatter; - private $logIteratorFactory; - - public function __construct(Formatter $formatter, LogIteratorFactory $logIteratorFactory) { + public function __construct( + private readonly Formatter $formatter, + private readonly LogIteratorFactory $logIteratorFactory, + ) { parent::__construct(); - $this->formatter = $formatter; - $this->logIteratorFactory = $logIteratorFactory; } #[\Override] @@ -64,14 +62,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int // 8 level, 18 for app, 26 for time, 6 for formatting $messageWidth = $totalWidth - 8 - 18 - 26 - 6; - $tableItems = array_map(function (array $logItem) use ($messageWidth) { - return [ - self::LEVELS[$logItem['level']], - wordwrap($logItem['app'], 18), - $this->formatter->formatMessage($logItem, $messageWidth) . "\n", - $logItem['time'], - ]; - }, $logItems); + $tableItems = array_map(fn (array $logItem) => [ + self::LEVELS[$logItem['level']], + wordwrap($logItem['app'], 18), + $this->formatter->formatMessage($logItem, $messageWidth) . "\n", + $logItem['time'], + ], $logItems); $io->table([ 'Level', 'App', diff --git a/lib/Command/Watch.php b/lib/Command/Watch.php index 3084a5511..5237ef900 100644 --- a/lib/Command/Watch.php +++ b/lib/Command/Watch.php @@ -24,13 +24,11 @@ class Watch extends Base { public const LEVELS = ['Debug', 'Info', 'Warning', 'Error', 'Fatal']; public const ALL_LEVELS = [0, 1, 2, 3, 4]; - private $formatter; - private $logIteratorFactory; - - public function __construct(Formatter $formatter, LogIteratorFactory $logIteratorFactory) { + public function __construct( + private readonly Formatter $formatter, + private readonly LogIteratorFactory $logIteratorFactory, + ) { parent::__construct(); - $this->formatter = $formatter; - $this->logIteratorFactory = $logIteratorFactory; } #[\Override] @@ -66,7 +64,7 @@ public function watch(bool $raw, OutputInterface $output): int { usleep(100 * 1000); try { $this->abortIfInterrupted(); - } catch (InterruptedException $e) { + } catch (InterruptedException) { break; } @@ -116,24 +114,12 @@ private function printItem(array $logItem, OutputInterface $output, int $message $logItem['time'], ]; - $partLines = array_map(function ($part) { - return explode("\n", $part); - }, $parts); - $lineCount = array_reduce($partLines, function (int $count, array $lines) { - return max($count, count($lines)); - }, 0); - $partLines = array_map(function (array $lines) use ($lineCount) { - return array_pad($lines, $lineCount, ''); - }, $partLines); - $partLines = array_map(function (array $lines, int $width) { - return array_map(function (string $line) use ($width) { - return str_pad($line, $width); - }, $lines); - }, $partLines, $widths); + $partLines = array_map(fn ($part) => explode("\n", $part), $parts); + $lineCount = array_reduce($partLines, fn (int $count, array $lines) => max($count, count($lines)), 0); + $partLines = array_map(fn (array $lines) => array_pad($lines, $lineCount, ''), $partLines); + $partLines = array_map(fn (array $lines, int $width) => array_map(fn (string $line) => str_pad($line, $width), $lines), $partLines, $widths); $lines = array_map(function (int $lineNumber) use ($partLines) { - $partsForLine = array_map(function (array $lines) use ($lineNumber) { - return $lines[$lineNumber]; - }, $partLines); + $partsForLine = array_map(fn (array $lines) => $lines[$lineNumber], $partLines); return implode(' ', $partsForLine); }, range(0, $lineCount - 1)); diff --git a/lib/Controller/LogController.php b/lib/Controller/LogController.php index 486c1c29a..1a5109a83 100644 --- a/lib/Controller/LogController.php +++ b/lib/Controller/LogController.php @@ -30,9 +30,9 @@ class LogController extends Controller { public function __construct( $appName, IRequest $request, - private LogIteratorFactory $logIteratorFactory, - private SettingsService $settingsService, - private LoggerInterface $logger, + private readonly LogIteratorFactory $logIteratorFactory, + private readonly SettingsService $settingsService, + private readonly LoggerInterface $logger, ) { parent::__construct($appName, $request); } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 14fabd8bf..9aab1a66c 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -23,16 +23,15 @@ class PageController extends Controller { public function __construct( - private IInitialState $initialState, - private SettingsService $settingsService, + private readonly IInitialState $initialState, + private readonly SettingsService $settingsService, ) { } /** - * @NoCSRFRequired - * * @return TemplateResponse */ + #[\OCP\AppFramework\Http\Attribute\NoCSRFRequired] public function index() { Util::addScript($this->appName, 'logreader-main'); Util::addStyle($this->appName, 'logreader-main'); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 608d00b3c..98f1a2306 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -27,9 +27,9 @@ class SettingsController extends ApiController { public function __construct( string $appName, IRequest $request, - private SettingsService $settingsService, - private IConfig $config, - private LoggerInterface $logger, + private readonly SettingsService $settingsService, + private readonly IConfig $config, + private readonly LoggerInterface $logger, ) { parent::__construct($appName, $request); } diff --git a/lib/Log/Console.php b/lib/Log/Console.php index 2584c149a..cb979af23 100644 --- a/lib/Log/Console.php +++ b/lib/Log/Console.php @@ -16,15 +16,16 @@ * Utility to write log messages to the console as they are emitted */ class Console extends LogDetails { - private int $level; - private int $terminalWidth; - private Formatter $formatter; + private readonly int $level; - public function __construct(Formatter $formatter, SystemConfig $config, string $level, int $terminalWidth) { + public function __construct( + private readonly Formatter $formatter, + SystemConfig $config, + string $level, + private readonly int $terminalWidth, + ) { parent::__construct($config); - $this->formatter = $formatter; $this->level = self::parseLogLevel($level); - $this->terminalWidth = $terminalWidth; } public function log(int $level, string $app, array $entry) { @@ -54,19 +55,13 @@ private static function parseLogLevel(string $level): int { return (int)$level; } - switch (strtoupper($level)) { - case 'DEBUG': - return 0; - case 'INFO': - return 1; - case 'WARN': - return 2; - case 'ERROR': - return 3; - case 'FATAL': - return 4; - default: - throw new \Exception("Unknown log level $level"); - } + return match (strtoupper($level)) { + 'DEBUG' => 0, + 'INFO' => 1, + 'WARN' => 2, + 'ERROR' => 3, + 'FATAL' => 4, + default => throw new \Exception("Unknown log level $level"), + }; } } diff --git a/lib/Log/Formatter.php b/lib/Log/Formatter.php index fcc906b9c..522978f64 100644 --- a/lib/Log/Formatter.php +++ b/lib/Log/Formatter.php @@ -9,10 +9,9 @@ namespace OCA\LogReader\Log; class Formatter { - private $root; - - public function __construct(string $root) { - $this->root = $root; + public function __construct( + private readonly string $root, + ) { } public function formatMessage($logItem, int $width) { @@ -29,9 +28,7 @@ private function formatException(array $e, int $width) { $largestIndex = count($e['Trace']) - 1; $largestIndexWidth = strlen((string)$largestIndex); $message = wordwrap($e['Exception'] . ': ' . $e['Message'] . ' at ' . $this->getFileAndLine($e), $width) . "\n\n"; - $message .= implode("\n", array_map(function ($index, $trace) use ($largestIndexWidth, $width) { - return $this->formatTraceLine($index, $trace, $largestIndexWidth, $width); - }, array_keys($e['Trace']), array_values($e['Trace']))); + $message .= implode("\n", array_map(fn ($index, $trace) => $this->formatTraceLine($index, $trace, $largestIndexWidth, $width), array_keys($e['Trace']), array_values($e['Trace']))); if (isset($e['Previous'])) { $message .= "\n\n" . 'Caused by ' . $this->formatException($e['Previous'], $width); @@ -58,9 +55,7 @@ private function formatTraceLine(int $index, array $trace, int $largestIndexWidt } else { return $whiteSpace . $index . '. ' . $this->getFileAndLine($trace, $argumentWidth) . "\n" . $argumentWhiteSpace . $method . "(\n" - . implode(",\n", array_map(function ($argumentLine) use ($argumentWhiteSpace) { - return $argumentWhiteSpace . ' ' . trim($argumentLine); - }, $arguments)) . "\n" + . implode(",\n", array_map(fn ($argumentLine) => $argumentWhiteSpace . ' ' . trim($argumentLine), $arguments)) . "\n" . $argumentWhiteSpace . ')'; } } @@ -80,14 +75,10 @@ private function formatArgument($argument, int $whiteSpace, int $depth = 0, bool if ($isObject) { $keyWhitespace = str_repeat(' ', $whiteSpace * ($depth + 1)); return $leadingSpace . "{\n" - . implode($glue, array_map(function ($key, $value) use ($whiteSpace, $depth, $keyWhitespace) { - return $keyWhitespace . $key . ':' . trim($this->formatArgument($value, $whiteSpace, $depth + 1)); - }, array_keys($argument), array_values($argument))) . ($whiteSpace ? "\n" : '') . $leadingSpace . '}'; + . implode($glue, array_map(fn ($key, $value) => $keyWhitespace . $key . ':' . trim($this->formatArgument($value, $whiteSpace, $depth + 1)), array_keys($argument), array_values($argument))) . ($whiteSpace ? "\n" : '') . $leadingSpace . '}'; } else { return $leadingSpace . "[\n" - . implode($glue, array_map(function ($value) use ($whiteSpace, $depth) { - return $this->formatArgument($value, $whiteSpace, $depth + 1); - }, $argument)) . ($whiteSpace ? "\n" : '') . $leadingSpace . ']'; + . implode($glue, array_map(fn ($value) => $this->formatArgument($value, $whiteSpace, $depth + 1), $argument)) . ($whiteSpace ? "\n" : '') . $leadingSpace . ']'; } } else { $value = json_encode($argument, $whiteSpace ? JSON_PRETTY_PRINT : 0); @@ -106,7 +97,7 @@ private function getFileAndLine(array $item, int $width = 64): string { $fileWidth = $width - strlen($postFix) - 4; $prefix = ''; $count = 0; - while (strlen($file) > $fileWidth && strpos($file, '/') !== false && $count < 20) { + while (strlen($file) > $fileWidth && str_contains($file, '/') && $count < 20) { $file = substr($file, strpos($file, '/') + 1); $prefix = '.../'; $count++; diff --git a/lib/Log/LogIterator.php b/lib/Log/LogIterator.php index 6a6569ad4..26122d740 100644 --- a/lib/Log/LogIterator.php +++ b/lib/Log/LogIterator.php @@ -12,12 +12,7 @@ * @template-implements \Iterator */ class LogIterator implements \Iterator { - /** - * @var resource - */ - private $handle; - private string $dateFormat; - private \DateTimeZone $timezone; + private readonly \DateTimeZone $timezone; private string $buffer = ''; private string $lastLine = ''; @@ -31,9 +26,11 @@ class LogIterator implements \Iterator { * @param string $dateFormat * @param string $timezone */ - public function __construct($handle, string $dateFormat, string $timezone) { - $this->handle = $handle; - $this->dateFormat = $dateFormat; + public function __construct( + private $handle, + private readonly string $dateFormat, + string $timezone, + ) { $this->timezone = new \DateTimeZone($timezone); $this->rewind(); } diff --git a/lib/Log/LogIteratorFactory.php b/lib/Log/LogIteratorFactory.php index 5f571bb6b..95fcc4ed5 100644 --- a/lib/Log/LogIteratorFactory.php +++ b/lib/Log/LogIteratorFactory.php @@ -18,8 +18,8 @@ */ class LogIteratorFactory { public function __construct( - private IConfig $config, - private ILogFactory $logFactory, + private readonly IConfig $config, + private readonly ILogFactory $logFactory, ) { } @@ -40,9 +40,7 @@ public function getLogIterator(array $levels): \Iterator { $handle = fopen($log->getLogFilePath(), 'rb'); if ($handle) { $iterator = new LogIterator($handle, $dateFormat, $timezone); - return new \CallbackFilterIterator($iterator, function ($logItem) use ($levels) { - return $logItem && in_array($logItem['level'], $levels); - }); + return new \CallbackFilterIterator($iterator, fn ($logItem) => $logItem && in_array($logItem['level'], $levels)); } else { throw new \Exception('Error while opening ' . $log->getLogFilePath()); } diff --git a/lib/Log/SearchFilter.php b/lib/Log/SearchFilter.php index 4d6f30ed0..855ec0c1a 100644 --- a/lib/Log/SearchFilter.php +++ b/lib/Log/SearchFilter.php @@ -30,7 +30,7 @@ public function __construct(\Iterator $iterator, string $query) { } private function formatLevel(int $level): string { - return isset($this->levels[$level]) ? $this->levels[$level] : 'Unknown'; + return $this->levels[$level] ?? 'Unknown'; } #[\Override] diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 43b7a6c48..ab55030e3 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -19,9 +19,9 @@ class Admin implements IDelegatedSettings { public function __construct( - private string $appName, - private IInitialState $initialState, - private SettingsService $settingsService, + private readonly string $appName, + private readonly IInitialState $initialState, + private readonly SettingsService $settingsService, ) { } diff --git a/lib/SetupChecks/LogErrors.php b/lib/SetupChecks/LogErrors.php index e02d7dab4..0a023f2f8 100644 --- a/lib/SetupChecks/LogErrors.php +++ b/lib/SetupChecks/LogErrors.php @@ -20,14 +20,14 @@ * @psalm-api */ class LogErrors implements ISetupCheck { - private const LEVEL_WARNING = 2; - private const LEVEL_ERROR = 3; - private const LEVEL_FATAL = 4; + private const int LEVEL_WARNING = 2; + private const int LEVEL_ERROR = 3; + private const int LEVEL_FATAL = 4; public function __construct( - private IL10N $l10n, - private IDateTimeFormatter $dateFormatter, - private LogIteratorFactory $logIteratorFactory, + private readonly IL10N $l10n, + private readonly IDateTimeFormatter $dateFormatter, + private readonly LogIteratorFactory $logIteratorFactory, ) { } diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..f8f3dc0d9 --- /dev/null +++ b/rector.php @@ -0,0 +1,28 @@ +withPaths([ + __DIR__ . '/appinfo', + __DIR__ . '/lib', + __DIR__ . '/templates', + __DIR__ . '/tests/Unit', + ]) + ->withPhpSets(php83: true) + ->withSets([ + NextcloudSets::NEXTCLOUD_35, + ]) + ->withSkip([ + // Rewrites to untyped IAppConfig::getValue()/setValue(); OCP only has typed getters/setters, so the type must be picked per call site + ReplaceInjectedMethodCallRector::class, + ]) + ->withTypeCoverageLevel(0); diff --git a/tests/Unit/Controller/SettingsControllerTest.php b/tests/Unit/Controller/SettingsControllerTest.php index 1ba5e3e52..84de0ae25 100644 --- a/tests/Unit/Controller/SettingsControllerTest.php +++ b/tests/Unit/Controller/SettingsControllerTest.php @@ -43,7 +43,7 @@ public function setUp(): void { $this->settingsService = $this->createMock(SettingsService::class); $this->config = $this->createMock(IConfig::class); /** @var LoggerInterface */ - $this->logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $this->logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)->getMock(); $this->request = $this->createMock(IRequest::class); $this->settingsController = new SettingsController( diff --git a/tests/Unit/Service/SettingsServiceTest.php b/tests/Unit/Service/SettingsServiceTest.php index 9e02677c3..1f8002bdc 100644 --- a/tests/Unit/Service/SettingsServiceTest.php +++ b/tests/Unit/Service/SettingsServiceTest.php @@ -96,9 +96,7 @@ public function testGetters(string $fn, bool $default, string $configKey, ?strin $this->config->expects($this->once()) ->method('getAppValue') ->with($this->equalTo('logreader'), $this->equalTo($configKey), $this->anything()) - ->willReturnCallback(function ($app, $key, $fallback) use ($default, $configValue) { - return $default ? $fallback : $configValue; - }); + ->willReturnCallback(fn ($app, $key, $fallback) => $default ? $fallback : $configValue); $this->assertEquals($this->settingsService->{$fn}(), $expected); } @@ -123,9 +121,7 @@ public function testGetAppSettings() { ->willReturn(4); $this->config->expects($this->any()) ->method('getAppValue') - ->willReturnCallback(function ($app, $key, $fallback) { - return $fallback; - }); + ->willReturnCallback(fn ($app, $key, $fallback) => $fallback); $this->assertEquals($this->settingsService->getAppSettings(), [ Constants::CONFIG_KEY_SHOWNLEVELS => Constants::LOGGING_LEVELS, @@ -148,9 +144,7 @@ public function testGetAppSettings_nonfile() { ->willReturn(2); $this->config->expects($this->any()) ->method('getAppValue') - ->willReturnCallback(function ($app, $key, $fallback) { - return $fallback; - }); + ->willReturnCallback(fn ($app, $key, $fallback) => $fallback); $this->assertEquals($this->settingsService->getAppSettings(), [ Constants::CONFIG_KEY_SHOWNLEVELS => Constants::LOGGING_LEVELS, diff --git a/vendor-bin/rector/composer.json b/vendor-bin/rector/composer.json new file mode 100644 index 000000000..02e7bae4f --- /dev/null +++ b/vendor-bin/rector/composer.json @@ -0,0 +1,12 @@ +{ + "config": { + "platform": { + "php": "8.3.16" + }, + "sort-packages": true + }, + "require-dev": { + "nextcloud/rector": "^0.5.2", + "rector/rector": "~2.7.0" + } +} diff --git a/vendor-bin/rector/composer.lock b/vendor-bin/rector/composer.lock new file mode 100644 index 000000000..ae5b39aa0 --- /dev/null +++ b/vendor-bin/rector/composer.lock @@ -0,0 +1,950 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "85fc46cff410001d42dcb1626f50422c", + "packages": [], + "packages-dev": [ + { + "name": "nextcloud/ocp", + "version": "v35.0.2", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "1f372f9d20bc38860d1d11a55c1c16293b759e6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/1f372f9d20bc38860d1d11a55c1c16293b759e6c", + "reference": "1f372f9d20bc38860d1d11a55c1c16293b759e6c", + "shasum": "" + }, + "require": { + "php": "~8.3 || ~8.4 || ~8.5", + "psr/clock": "^1.0", + "psr/container": "^2.0.2", + "psr/event-dispatcher": "^1.0", + "psr/http-client": "^1.0.3", + "psr/log": "^3.0.2", + "symfony/polyfill-intl-normalizer": "^1.38", + "symfony/polyfill-php84": "^1.38", + "symfony/polyfill-php85": "^1.41", + "symfony/polyfill-php86": "^1.41" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-stable35": "35.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + }, + { + "name": "Joas Schilling", + "email": "coding@schilljs.com" + } + ], + "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/v35.0.2" + }, + "time": "2026-10-09T03:32:50+00:00" + }, + { + "name": "nextcloud/rector", + "version": "v0.5.2", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-libraries/rector.git", + "reference": "9967d62256cd7507f1104491fb2c427456481b56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-libraries/rector/zipball/9967d62256cd7507f1104491fb2c427456481b56", + "reference": "9967d62256cd7507f1104491fb2c427456481b56", + "shasum": "" + }, + "require": { + "nextcloud/ocp": ">=27", + "php": "^8.1", + "rector/rector": "^2.0.4", + "webmozart/assert": "^1.11" + }, + "require-dev": { + "phpunit/phpunit": "^10.5", + "ramsey/devtools": "^2.0" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/devtools": { + "memory-limit": "-1", + "command-prefix": "dev" + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "OCP\\": "vendor/nextcloud/ocp/OCP", + "Nextcloud\\Rector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at", + "homepage": "https://wuc.me" + } + ], + "description": "Rector upgrade rules for Nextcloud", + "keywords": [ + "nextcloud", + "refactoring" + ], + "support": { + "issues": "https://github.com/nextcloud-libraries/rector/issues", + "source": "https://github.com/nextcloud-libraries/rector/tree/v0.5.2" + }, + "time": "2026-06-15T06:39:12+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.3.3", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4b57dc8ca7456b635d1661845f44bc060b02c6b0", + "reference": "4b57dc8ca7456b635d1661845f44bc060b02c6b0", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-10-27T14:06:26+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "rector/rector", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "4d6a39b7fff302cac0ce90cd959a53c2f6a32776" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/4d6a39b7fff302cac0ce90cd959a53c2f6a32776", + "reference": "4d6a39b7fff302cac0ce90cd959a53c2f6a32776", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.3.0" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-10-06T08:46:45+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-23T22:02:09+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-23T22:01:09+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/86a42cbcc07adc331481a5ab310a4024bfc227f6", + "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-25T15:46:47+00:00" + }, + { + "name": "symfony/polyfill-php86", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/8840df45b24f33a49dc419fa18dc0057231ae1d8", + "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-25T15:46:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.3.16" + }, + "plugin-api-version": "2.6.0" +}