diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index de9023b..693a507 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -52,6 +52,9 @@ jobs:
- name: Run PHPStan
run: vendor/bin/phpstan analyse
+ - name: Run Psalm
+ run: vendor/bin/psalm --no-cache
+
- name: Run Rector
run: vendor/bin/rector process --ansi --dry-run --config rector.php Kununu/ tests/
diff --git a/Kununu/ArchitectureSniffer/ArchitectureSniffer.php b/Kununu/ArchitectureSniffer/ArchitectureSniffer.php
index c9ba1d4..e370a06 100644
--- a/Kununu/ArchitectureSniffer/ArchitectureSniffer.php
+++ b/Kununu/ArchitectureSniffer/ArchitectureSniffer.php
@@ -73,7 +73,7 @@ public function testArchitecture(): iterable
$architecture,
static fn(array $group) => !array_filter(
is_array($group[Group::INCLUDES_KEY] ?? null) ? $group[Group::INCLUDES_KEY] : [],
- static fn($include) => str_starts_with((string) $include, 'App\\')
+ static fn(mixed $include): bool => str_starts_with((string) $include, 'App\\')
)
);
diff --git a/Kununu/ArchitectureSniffer/Helper/TypeChecker.php b/Kununu/ArchitectureSniffer/Helper/TypeChecker.php
index 6f5956e..677e5a8 100644
--- a/Kununu/ArchitectureSniffer/Helper/TypeChecker.php
+++ b/Kununu/ArchitectureSniffer/Helper/TypeChecker.php
@@ -13,13 +13,7 @@ public static function isArrayKeysOfStrings(mixed $arr): bool
return false;
}
- foreach (array_keys($arr) as $key) {
- if (!is_string($key)) {
- return false;
- }
- }
-
- return true;
+ return array_all(array_keys($arr), static fn($key) => is_string($key));
}
public static function isArrayOfStrings(mixed $arr): bool
diff --git a/Kununu/CsFixer/Command/CsFixerCommand.php b/Kununu/CsFixer/Command/CsFixerCommand.php
index 178788f..5e0fded 100644
--- a/Kununu/CsFixer/Command/CsFixerCommand.php
+++ b/Kununu/CsFixer/Command/CsFixerCommand.php
@@ -76,7 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return self::FAILURE;
}
- $configSource = $input->getOption(self::OPTION_CONFIG) ?: __DIR__ . '/../../../php-cs-fixer.php';
+ $configSource = $input->getOption(self::OPTION_CONFIG) ?? __DIR__ . '/../../../php-cs-fixer.php';
$configPath = realpath($configSource);
if ($configPath === false || !is_file($configPath)) {
diff --git a/Kununu/CsFixer/Command/CsFixerGitHookCommand.php b/Kununu/CsFixer/Command/CsFixerGitHookCommand.php
index 2ef8db3..e622566 100644
--- a/Kununu/CsFixer/Command/CsFixerGitHookCommand.php
+++ b/Kununu/CsFixer/Command/CsFixerGitHookCommand.php
@@ -140,7 +140,9 @@ private function resolveVendorDir(string $rootGitPath): string
foreach ($candidates as $candidate) {
if (is_dir($candidate)) {
- return realpath($candidate) ?: $candidate;
+ $resolved = realpath($candidate);
+
+ return $resolved !== false ? $resolved : $candidate;
}
}
diff --git a/Kununu/CsFixer/CsFixerPlugin.php b/Kununu/CsFixer/CsFixerPlugin.php
index 5678c95..18bee40 100644
--- a/Kununu/CsFixer/CsFixerPlugin.php
+++ b/Kununu/CsFixer/CsFixerPlugin.php
@@ -19,8 +19,8 @@
final class CsFixerPlugin implements PluginInterface, EventSubscriberInterface, Capable
{
- private Composer $composer;
- private IOInterface $io;
+ private ?Composer $composer = null;
+ private ?IOInterface $io = null;
public static function getSubscribedEvents(): array
{
@@ -54,6 +54,10 @@ public function getCapabilities(): array
/** @throws ExceptionInterface */
public function addCsFixerGitHooks(): void
{
+ if ($this->composer === null || $this->io === null) {
+ return;
+ }
+
$command = new CsFixerGitHookCommand();
$command->setComposer($this->composer);
$command->setIO($this->io);
diff --git a/bin/code-tools b/bin/code-tools
index 178cf37..6bb4b03 100755
--- a/bin/code-tools
+++ b/bin/code-tools
@@ -20,6 +20,7 @@ fi
readonly CONFIG_FILES=(
"dist/php-cs-fixer.php.dist"
"dist/phpcs.xml.dist"
+ "dist/psalm.xml.dist"
"dist/rector.php.dist"
"dist/.editorconfig.dist"
)
@@ -32,6 +33,7 @@ Usage: vendor/bin/code-tools publish:config [tool-name]
Valid tool names:
cs-fixer => copies dist/php-cs-fixer.php.dist
code-sniffer => copies dist/phpcs.xml.dist
+ psalm => copies dist/psalm.xml.dist
rector => copies dist/rector.php.dist
editorconfig => copies dist/.editorconfig.dist
EOF
@@ -79,6 +81,9 @@ case "$tool_name" in
code-sniffer)
copy_config_file "dist/phpcs.xml.dist"
;;
+ psalm)
+ copy_config_file "dist/psalm.xml.dist"
+ ;;
rector)
copy_config_file "dist/rector.php.dist"
;;
diff --git a/composer-dependency-analyser.php b/composer-dependency-analyser.php
index 236115a..9ae1971 100644
--- a/composer-dependency-analyser.php
+++ b/composer-dependency-analyser.php
@@ -20,8 +20,10 @@
[
'friendsofphp/php-cs-fixer',
'phpstan/phpstan',
+ 'psalm/plugin-symfony',
'rector/rector',
'squizlabs/php_codesniffer',
+ 'vimeo/psalm',
],
[ErrorType::UNUSED_DEPENDENCY]
);
diff --git a/composer.json b/composer.json
index 1874d6d..420f5fc 100644
--- a/composer.json
+++ b/composer.json
@@ -16,11 +16,13 @@
"friendsofphp/php-cs-fixer": "^3.93",
"phpat/phpat": "^0.11.4",
"phpstan/phpstan": "^2.1",
+ "psalm/plugin-symfony": "^5.3",
"rector/rector": "^2.0",
"squizlabs/php_codesniffer": "^3.10",
"symfony/console": "^6.4 || ^7.4",
"symfony/process": "^6.4 || ^7.4",
- "symfony/yaml": "^6.4 || ^7.4"
+ "symfony/yaml": "^6.4 || ^7.4",
+ "vimeo/psalm": "^6.16"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.50",
@@ -57,12 +59,15 @@
"@cs-check",
"@stan",
"@rector",
+ "@psalm",
"@test-unit"
],
"cs-check": "phpcs --standard=phpcs.xml Kununu/ tests/",
"cs-fix": "phpcbf --standard=phpcs.xml Kununu/ tests/",
"cs-fixer-check": "php-cs-fixer check --config=php-cs-fixer.php",
"cs-fixer-fix": "php-cs-fixer fix --config=php-cs-fixer.php",
+ "psalm": "psalm --no-cache",
+ "psalm-baseline": "psalm --no-cache --set-baseline=psalm-baseline.xml",
"rector": "rector process --dry-run Kununu/ tests/",
"rector-fix": "rector process Kununu/ tests/",
"stan": "phpstan analyze",
diff --git a/dist/psalm.xml.dist b/dist/psalm.xml.dist
new file mode 100644
index 0000000..cba18c8
--- /dev/null
+++ b/dist/psalm.xml.dist
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 0000000..6e5b0ae
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+