From 2f75201b9a3339aaa9c9fb09ea6549875b70c501 Mon Sep 17 00:00:00 2001 From: Christian Scheb Date: Tue, 4 Feb 2025 19:54:20 +0100 Subject: [PATCH] Address Psalm issues --- psalm.xml | 4 ++-- src/analyzer/Cli/AnalyzeCommand.php | 11 +++++++++-- src/analyzer/Config/YamlConfigProvider.php | 6 +++++- src/analyzer/Model/AnalyzerResult.php | 8 +++++--- .../Report/Checkstyle/CheckstyleReportGenerator.php | 1 + .../Report/Html/Renderer/PhpFileFormatter.php | 4 ++++ src/analyzer/Stock/ParserTombstoneProvider.php | 1 + src/analyzer/Stock/TombstoneExtractor.php | 4 ++++ src/analyzer/Stock/TombstoneNodeVisitor.php | 2 +- src/core/FinderFacade.php | 3 +++ src/core/Format/AnalyzerLogFormat.php | 5 ++++- src/core/Model/RootPath.php | 1 + src/logger/Formatter/JsonFormatter.php | 10 ++++++---- src/logger/Handler/StreamHandler.php | 2 +- 14 files changed, 47 insertions(+), 15 deletions(-) diff --git a/psalm.xml b/psalm.xml index 1a6f1a9..6107172 100644 --- a/psalm.xml +++ b/psalm.xml @@ -28,13 +28,13 @@ - + - + diff --git a/src/analyzer/Cli/AnalyzeCommand.php b/src/analyzer/Cli/AnalyzeCommand.php index 41a647a..0bc23e4 100644 --- a/src/analyzer/Cli/AnalyzeCommand.php +++ b/src/analyzer/Cli/AnalyzeCommand.php @@ -71,8 +71,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function doExecute(): void { - /** @var string $configFile */ - $configFile = $this->input->getOption('config') ?? getcwd().DIRECTORY_SEPARATOR.'tombstone.yml'; + /** @var string|null $configFile */ + $configFile = $this->input->getOption('config'); + if (null === $configFile) { + if (!($cwd = getcwd())) { + throw new \RuntimeException('Could not determine current working directory, please provide a configuration file path.'); + } + $configFile = $cwd.DIRECTORY_SEPARATOR.'tombstone.yml'; + } + if (!file_exists($configFile)) { throw new \InvalidArgumentException(\sprintf('Could not find configuration file %s', $configFile)); } diff --git a/src/analyzer/Config/YamlConfigProvider.php b/src/analyzer/Config/YamlConfigProvider.php index 985ab34..a4a8c73 100644 --- a/src/analyzer/Config/YamlConfigProvider.php +++ b/src/analyzer/Config/YamlConfigProvider.php @@ -28,9 +28,13 @@ class YamlConfigProvider implements ConfigProviderInterface public function __construct(string $configFile) { $this->configFile = $configFile; + $realpath = realpath($this->configFile); + if (false === $realpath) { + throw new \InvalidArgumentException("Config file '$this->configFile' is not a valid file path."); + } // Make all paths relative to config file path - $this->rootPath = new RootPath(\dirname(realpath($this->configFile))); + $this->rootPath = new RootPath(\dirname($realpath)); } public function readConfiguration(): array diff --git a/src/analyzer/Model/AnalyzerResult.php b/src/analyzer/Model/AnalyzerResult.php index 402085a..2f2a454 100644 --- a/src/analyzer/Model/AnalyzerResult.php +++ b/src/analyzer/Model/AnalyzerResult.php @@ -116,10 +116,12 @@ private function writeResultDirectoryTree(array &$tree, array $pathSegments, Ana $tree['files'][] = $fileResult; } else { $pathPart = array_shift($pathSegments); - if (!isset($tree['dirs'][$pathPart])) { - $tree['dirs'][$pathPart] = ['dirs' => [], 'files' => []]; + if (null !== $pathPart) { + if (!isset($tree['dirs'][$pathPart])) { + $tree['dirs'][$pathPart] = ['dirs' => [], 'files' => []]; + } + $this->writeResultDirectoryTree($tree['dirs'][$pathPart], $pathSegments, $fileResult); } - $this->writeResultDirectoryTree($tree['dirs'][$pathPart], $pathSegments, $fileResult); } } diff --git a/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php b/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php index 29c97f7..59570a0 100644 --- a/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php +++ b/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php @@ -81,6 +81,7 @@ private function getCalledBy(Tombstone $tombstone): string return ''; } + /** @psalm-suppress PossiblyNullReference Handled in the if statement above */ $invoker = array_shift($vampires)->getInvoker(); $calledBy = \sprintf(' by "%s"', null !== $invoker ? $invoker : 'global scope'); diff --git a/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php b/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php index a51d38e..aa58ab2 100644 --- a/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php +++ b/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php @@ -28,6 +28,10 @@ public function __construct(PhpSyntaxHighlighter $highlighter) public function formatFile(string $file): array { $buffer = file_get_contents($file); + if (false === $buffer) { + return []; + } + $tokens = token_get_all($buffer); $fileEndsWithNewLine = "\n" === substr($buffer, -1); unset($buffer); diff --git a/src/analyzer/Stock/ParserTombstoneProvider.php b/src/analyzer/Stock/ParserTombstoneProvider.php index a871cab..f84b554 100644 --- a/src/analyzer/Stock/ParserTombstoneProvider.php +++ b/src/analyzer/Stock/ParserTombstoneProvider.php @@ -44,6 +44,7 @@ public static function create(array $config, ConsoleOutputInterface $consoleOutp if (method_exists(ParserFactory::class, 'createForVersion')) { $parser = (new ParserFactory())->createForVersion(PhpVersion::getHostVersion()); } else { + /** @psalm-suppress UndefinedConstant Backwards compatibility for php-parser v4 */ $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); } $traverser = new NodeTraverser(); diff --git a/src/analyzer/Stock/TombstoneExtractor.php b/src/analyzer/Stock/TombstoneExtractor.php index 1f4bcf6..c8dd0da 100644 --- a/src/analyzer/Stock/TombstoneExtractor.php +++ b/src/analyzer/Stock/TombstoneExtractor.php @@ -66,6 +66,10 @@ private function parseSourceCode(string $absoluteFilePath): void { try { $content = file_get_contents($absoluteFilePath); + if (false === $content) { + throw new TombstoneExtractorException(\sprintf('File "%s" could not be read.', $absoluteFilePath)); + } + $stmts = $this->parser->parse($content); if (null === $stmts) { throw new TombstoneExtractorException(\sprintf('PHP code in "%s" could not be parsed.', $absoluteFilePath)); diff --git a/src/analyzer/Stock/TombstoneNodeVisitor.php b/src/analyzer/Stock/TombstoneNodeVisitor.php index c7e98c3..6be0dd5 100644 --- a/src/analyzer/Stock/TombstoneNodeVisitor.php +++ b/src/analyzer/Stock/TombstoneNodeVisitor.php @@ -179,7 +179,7 @@ private function extractArguments(array $args): array if ($arg instanceof Node\VariadicPlaceholder) { break; // Can't extract from ...$var arguments } elseif ($arg->value instanceof String_) { - /** @psalm-suppress RedundantCastGivenDocblockType */ + /** @psalm-suppress RedundantCast */ $params[] = (string) $arg->value->value; } else { $params[] = null; diff --git a/src/core/FinderFacade.php b/src/core/FinderFacade.php index 32a81db..4a834cb 100644 --- a/src/core/FinderFacade.php +++ b/src/core/FinderFacade.php @@ -25,6 +25,9 @@ public function __construct(array $items = [], array $excludes = [], array $name } /** + * @psalm-suppress InvalidReturnType + * @psalm-suppress InvalidReturnStatement + * * @return string[] */ public function findFiles(): array diff --git a/src/core/Format/AnalyzerLogFormat.php b/src/core/Format/AnalyzerLogFormat.php index bb60035..73bf48c 100644 --- a/src/core/Format/AnalyzerLogFormat.php +++ b/src/core/Format/AnalyzerLogFormat.php @@ -37,7 +37,8 @@ class AnalyzerLogFormat public static function vampireToLog(Vampire $vampire): string { - return json_encode([ + /** @var string $encoded */ + $encoded = json_encode([ self::FIELD_VERSION => self::CURRENT_VERSION, self::FIELD_FUNCTION_NAME => $vampire->getFunctionName(), self::FIELD_ARGUMENTS => $vampire->getArguments(), @@ -49,6 +50,8 @@ public static function vampireToLog(Vampire $vampire): string self::FIELD_INVOCATION_DATE => $vampire->getInvocationDate(), self::FIELD_INVOKER => $vampire->getInvoker(), ]); + + return $encoded; } private static function encodeStackTrace(StackTrace $stackTrace): array diff --git a/src/core/Model/RootPath.php b/src/core/Model/RootPath.php index 441b266..c779faa 100644 --- a/src/core/Model/RootPath.php +++ b/src/core/Model/RootPath.php @@ -73,6 +73,7 @@ private function createRelativePath(string $path): RelativeFilePath return new RelativeFilePath('', $this); } // Remove leading "./" + /** @var string $path */ $path = preg_replace('#^(\\./)+#', '', $path); } diff --git a/src/logger/Formatter/JsonFormatter.php b/src/logger/Formatter/JsonFormatter.php index 439cb94..b486d7a 100644 --- a/src/logger/Formatter/JsonFormatter.php +++ b/src/logger/Formatter/JsonFormatter.php @@ -12,16 +12,18 @@ class JsonFormatter implements FormatterInterface { public function format(Vampire $vampire): string { - return json_encode([ + /** @var string $encoded */ + $encoded = json_encode([ 'arguments' => $vampire->getArguments(), 'file' => $vampire->getFile()->getReferencePath(), 'line' => $vampire->getLine(), 'method' => $vampire->getMethod(), 'stackTrace' => $this->getStackTraceValues($vampire->getStackTrace()), 'metadata' => $vampire->getMetadata(), - 'invocationDate' => $vampire->getInvocationDate(), - 'invoker' => $vampire->getInvoker(), - ]).PHP_EOL; + 'invocationDate' => $vampire->getInvocationDate(), 'invoker' => $vampire->getInvoker(), + ]); + + return $encoded.PHP_EOL; } private function getStackTraceValues(StackTrace $stackTrace): array diff --git a/src/logger/Handler/StreamHandler.php b/src/logger/Handler/StreamHandler.php index 5c3bb98..6efd61e 100644 --- a/src/logger/Handler/StreamHandler.php +++ b/src/logger/Handler/StreamHandler.php @@ -15,7 +15,7 @@ class StreamHandler extends AbstractHandler { /** - * @var resource|closed-resource|null + * @var resource|closed-resource|false|null */ protected $stream;