Browse Source

RFC: Add basic PHPStan validation for split packages. (#17412)

* Add basic PHPStan validation for split packages.

* Enable scripts.

* Add gitattributes
Mark Scherer 2 years ago
parent
commit
b4814c6ccb

+ 5 - 0
.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

+ 8 - 0
.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

+ 56 - 0
contrib/validate-split-packages-phpstan.php

@@ -0,0 +1,56 @@
+#!/usr/bin/php -q
+<?php
+declare(strict_types=1);
+
+/*
+ * Validate split packages through PHPStan.
+ */
+
+$options = [
+    __DIR__ . '/../vendor/autoload.php',
+    __DIR__ . '/vendor/autoload.php',
+];
+if (!empty($_SERVER['PWD'])) {
+    array_unshift($options, $_SERVER['PWD'] . '/vendor/autoload.php');
+}
+
+foreach ($options as $file) {
+    if (file_exists($file)) {
+        define('COMPOSER_INSTALL', $file);
+
+        break;
+    }
+}
+require COMPOSER_INSTALL;
+
+$path = dirname(__DIR__) . DS . 'src' . DS;
+$di = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
+$iterator = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::LEAVES_ONLY);
+/** @var array<\SplFileInfo> $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);

+ 1 - 1
contrib/validate-split-packages.php

@@ -29,7 +29,7 @@ $path = dirname(__DIR__) . DS . 'src' . DS;
 $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;

+ 6 - 5
psalm.xml

@@ -13,9 +13,10 @@
     errorBaseline="psalm-baseline.xml"
 >
     <projectFiles>
-        <directory name="src" />
+        <directory name="src/"/>
         <ignoreFiles>
-            <directory name="vendor" />
+            <directory name="vendor/"/>
+            <directory name="src/Validation/tests/"/>
         </ignoreFiles>
     </projectFiles>
 
@@ -40,9 +41,9 @@
         </UndefinedDocblockClass>
         <UndefinedConstant>
             <errorLevel type="suppress">
-                <file name="src/Cache/Engine/ApcuEngine.php" />
-                <file name="src/Database/Driver/Sqlserver.php" />
-                <file name="src/Database/Statement/SqlserverStatement.php" />
+                <file name="src/Cache/Engine/ApcuEngine.php"/>
+                <file name="src/Database/Driver/Sqlserver.php"/>
+                <file name="src/Database/Statement/SqlserverStatement.php"/>
             </errorLevel>
         </UndefinedConstant>
         <RedundantPropertyInitializationCheck errorLevel="suppress"/>

+ 10 - 0
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

+ 4 - 0
src/Validation/composer.json

@@ -34,5 +34,9 @@
         "psr-4": {
             "Cake\\Validation\\": "."
         }
+    },
+    "require-dev": {
+        "cakephp/i18n": "^5.0",
+        "phpstan/phpstan": "^1.10"
     }
 }

+ 11 - 0
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/

+ 68 - 0
src/Validation/tests/phpstan-bootstrap.php

@@ -0,0 +1,68 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Cake\Core\Configure;
+
+if (is_file('vendor/autoload.php')) {
+    require_once 'vendor/autoload.php';
+} else {
+    require_once dirname(__DIR__) . '/vendor/autoload.php';
+}
+
+if (!defined('DS')) {
+    define('DS', DIRECTORY_SEPARATOR);
+}
+define('ROOT', dirname(__DIR__));
+define('APP_DIR', 'TestApp');
+
+define('TMP', sys_get_temp_dir() . DS);
+define('LOGS', TMP . 'logs' . DS);
+define('CACHE', TMP . 'cache' . DS);
+define('SESSIONS', TMP . 'sessions' . DS);
+
+define('CAKE_CORE_INCLUDE_PATH', ROOT);
+define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+define('CAKE', CORE_PATH . 'src' . DS);
+define('CORE_TESTS', CORE_PATH . 'tests' . DS);
+define('CORE_TEST_CASES', CORE_TESTS . 'TestCase');
+define('TEST_APP', CORE_TESTS . 'test_app' . DS);
+
+// Point app constants to the test app.
+define('APP', TEST_APP . 'TestApp' . DS);
+define('WWW_ROOT', TEST_APP . 'webroot' . DS);
+define('CONFIG', TEST_APP . 'config' . DS);
+
+// phpcs:disable
+@mkdir(LOGS);
+@mkdir(SESSIONS);
+@mkdir(CACHE);
+@mkdir(CACHE . 'views');
+@mkdir(CACHE . 'models');
+// phpcs:enable
+
+require_once ROOT . DS . 'vendor/cakephp/core/functions.php';
+
+date_default_timezone_set('UTC');
+mb_internal_encoding('UTF-8');
+
+Configure::write('debug', true);
+Configure::write('App', [
+    'namespace' => 'App',
+    'encoding' => 'UTF-8',
+]);
+
+ini_set('intl.default_locale', 'en_US');
+ini_set('session.gc_divisor', '1');
+ini_set('assert.exception', '1');