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;