diff --git a/.gitattributes b/.gitattributes
index aa4dad020e7..553a9d0c993 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -43,3 +43,8 @@ phpstan-baseline.neon export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore
+
+# Split package files
+src/Validation/.gitattributes export-ignore
+src/Validation/phpstan.neon.dist export-ignore
+src/Validation/tests/ export-ignore
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 37e7787fbc5..09328c1c036 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -126,6 +126,14 @@ jobs:
if: always()
run: php contrib/validate-deprecation-aliases.php
+ - name: Run composer.json validation for split packages
+ if: always()
+ run: php contrib/validate-split-packages.php
+
+ - name: Run PHPStan for split packages
+ if: always()
+ run: php contrib/validate-split-packages-phpstan.php
+
- name: Prefer lowest check
if: matrix.prefer-lowest == 'prefer-lowest'
run: composer require --dev dereuromark/composer-prefer-lowest && vendor/bin/validate-prefer-lowest -m
diff --git a/contrib/validate-split-packages-phpstan.php b/contrib/validate-split-packages-phpstan.php
new file mode 100644
index 00000000000..0135b2799ef
--- /dev/null
+++ b/contrib/validate-split-packages-phpstan.php
@@ -0,0 +1,56 @@
+#!/usr/bin/php -q
+ $iterator */
+$iterator = new RegexIterator($iterator, '~/src/\w+/composer.json$~');
+
+$packages = [];
+$code = 0;
+foreach ($iterator as $file) {
+ $filePath = $file->getPath();
+ $package = substr($filePath, strrpos($filePath, '/') + 1);
+ $packages[$filePath . '/'] = $package;
+}
+ksort($packages);
+
+$issues = [];
+foreach ($packages as $path => $package) {
+ // For now, demo only
+ if (!file_exists($path . 'phpstan.neon.dist')) {
+ continue;
+ }
+
+ $exitCode = null;
+ exec('cd ' . $path . ' && composer install && vendor/bin/phpstan analyze ./', $output, $exitCode);
+ if ($exitCode !== 0) {
+ $code = $exitCode;
+ }
+ exec('cd ' . $path . ' && rm composer.lock && rm -rf vendor');
+}
+
+exit($code);
diff --git a/contrib/validate-split-packages.php b/contrib/validate-split-packages.php
index ff9647256ed..a55d6c1a848 100644
--- a/contrib/validate-split-packages.php
+++ b/contrib/validate-split-packages.php
@@ -29,7 +29,7 @@
$di = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::LEAVES_ONLY);
/** @var array<\SplFileInfo> $iterator */
-$iterator = new RegexIterator($iterator, '~composer.json$~');
+$iterator = new RegexIterator($iterator, '~/src/\w+/composer.json$~');
$packages = [];
$code = 0;
diff --git a/psalm.xml b/psalm.xml
index 532cf25c963..fe10e07dddd 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -13,9 +13,10 @@
errorBaseline="psalm-baseline.xml"
>
-
+
-
+
+
@@ -40,9 +41,9 @@
-
-
-
+
+
+
diff --git a/src/Validation/.gitattributes b/src/Validation/.gitattributes
new file mode 100644
index 00000000000..0086560d10e
--- /dev/null
+++ b/src/Validation/.gitattributes
@@ -0,0 +1,10 @@
+# Define the line ending behavior of the different file extensions
+# Set default behavior, in case users don't have core.autocrlf set.
+* text text=auto eol=lf
+
+.php diff=php
+
+# Remove files for archives generated using `git archive`
+.gitattributes export-ignore
+phpstan.neon.dist export-ignore
+tests/ export-ignore
diff --git a/src/Validation/composer.json b/src/Validation/composer.json
index c6ab346d9dd..3e20e90521f 100644
--- a/src/Validation/composer.json
+++ b/src/Validation/composer.json
@@ -34,5 +34,9 @@
"psr-4": {
"Cake\\Validation\\": "."
}
+ },
+ "require-dev": {
+ "cakephp/i18n": "^5.0",
+ "phpstan/phpstan": "^1.10"
}
}
diff --git a/src/Validation/phpstan.neon.dist b/src/Validation/phpstan.neon.dist
new file mode 100644
index 00000000000..1e0cb6cd8a1
--- /dev/null
+++ b/src/Validation/phpstan.neon.dist
@@ -0,0 +1,11 @@
+parameters:
+ level: 8
+ checkMissingIterableValueType: false
+ checkGenericClassInNonGenericObjectType: false
+ treatPhpDocTypesAsCertain: false
+ bootstrapFiles:
+ - tests/phpstan-bootstrap.php
+ paths:
+ - ./
+ excludePaths:
+ - vendor/
diff --git a/src/Validation/tests/phpstan-bootstrap.php b/src/Validation/tests/phpstan-bootstrap.php
new file mode 100644
index 00000000000..f0fe4865ec7
--- /dev/null
+++ b/src/Validation/tests/phpstan-bootstrap.php
@@ -0,0 +1,68 @@
+ 'App',
+ 'encoding' => 'UTF-8',
+]);
+
+ini_set('intl.default_locale', 'en_US');
+ini_set('session.gc_divisor', '1');
+ini_set('assert.exception', '1');