Commit 11f4ea951a9 for woocommerce
commit 11f4ea951a9c1c35eef2469ed08d87fd63698129
Author: daledupreez <dale@automattic.com>
Date: Mon Jun 22 19:11:56 2026 +0200
Add PHPStan validation for woocommerce-subscriptions-engine (#65876)
- Add level-9 PHPStan analysis for PHP 7.4 and 8.4, wired into the package CI
- Isolate PHPStan and its stub packages in a tasks/phpstan/ sub-project (own composer.json + run-phpstan.sh), keeping the stubs out of the package vendor (mirrors email-editor)
- Mark the PHPStan CI jobs optional with no baseline, so they report without blocking the required gate while existing errors are fixed in follow-ups
- Exclude the PHPStan stubs directory from phpcs so the Lint job passes
diff --git a/packages/php/woocommerce-subscriptions-engine/.gitignore b/packages/php/woocommerce-subscriptions-engine/.gitignore
new file mode 100644
index 00000000000..7ebad1eb4da
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/.gitignore
@@ -0,0 +1,3 @@
+tasks/phpstan/vendor/
+tasks/phpstan/temp/
+
diff --git a/packages/php/woocommerce-subscriptions-engine/changelog/add-phpstan-validation-for-woocommerce-subscriptions-engine b/packages/php/woocommerce-subscriptions-engine/changelog/add-phpstan-validation-for-woocommerce-subscriptions-engine
new file mode 100644
index 00000000000..c8fc7423079
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/changelog/add-phpstan-validation-for-woocommerce-subscriptions-engine
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add PHPStan configuration and CI
diff --git a/packages/php/woocommerce-subscriptions-engine/composer.lock b/packages/php/woocommerce-subscriptions-engine/composer.lock
index be315b9902f..a06944da84d 100644
--- a/packages/php/woocommerce-subscriptions-engine/composer.lock
+++ b/packages/php/woocommerce-subscriptions-engine/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "c99e9fbb764865f370ade0b553c9729f",
+ "content-hash": "fab9c2150bd5fa3e1cfd83e72bb2844d",
"packages": [],
"packages-dev": [
{
@@ -3608,5 +3608,5 @@
"platform-overrides": {
"php": "7.4"
},
- "plugin-api-version": "2.9.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/packages/php/woocommerce-subscriptions-engine/package.json b/packages/php/woocommerce-subscriptions-engine/package.json
index f28cee36760..436a2d7e124 100644
--- a/packages/php/woocommerce-subscriptions-engine/package.json
+++ b/packages/php/woocommerce-subscriptions-engine/package.json
@@ -8,6 +8,9 @@
"lint:fix": "pnpm --if-present '/^lint:fix:lang:.*$/'",
"lint:fix:lang:php": "composer run-script phpcbf",
"lint:lang:php": "composer run-script phpcs",
+ "phpstan": "cd tasks/phpstan && ./run-phpstan.sh",
+ "phpstan:php7": "cd tasks/phpstan && ./run-phpstan.sh php7",
+ "phpstan:php8": "cd tasks/phpstan && ./run-phpstan.sh",
"test:php:ci": "pnpm test:php:unit && pnpm test:php:integration",
"test:php:unit": "composer run-script test:unit",
"test:php:integration": "composer run-script test:integration",
@@ -50,6 +53,36 @@
"pull_request",
"push"
]
+ },
+ {
+ "name": "PHPStan: PHP 7.4",
+ "testType": "static:analysis",
+ "command": "phpstan:php7",
+ "optional": true,
+ "changes": [
+ "**/*.php",
+ "**/*.neon",
+ "tasks/phpstan/**"
+ ],
+ "events": [
+ "pull_request",
+ "push"
+ ]
+ },
+ {
+ "name": "PHPStan: PHP 8.4",
+ "testType": "static:analysis",
+ "command": "phpstan:php8",
+ "optional": true,
+ "changes": [
+ "**/*.php",
+ "**/*.neon",
+ "tasks/phpstan/**"
+ ],
+ "events": [
+ "pull_request",
+ "push"
+ ]
}
]
}
diff --git a/packages/php/woocommerce-subscriptions-engine/phpcs.xml b/packages/php/woocommerce-subscriptions-engine/phpcs.xml
index 96ff73060da..0c10b25c26d 100644
--- a/packages/php/woocommerce-subscriptions-engine/phpcs.xml
+++ b/packages/php/woocommerce-subscriptions-engine/phpcs.xml
@@ -71,6 +71,9 @@
<!-- Skip the PHPStan temp directory -->
<exclude-pattern>tasks/phpstan/temp/*</exclude-pattern>
+ <!-- Skip the PHPStan stubs: they mirror external symbols verbatim, so WPCS naming/structure rules do not apply. -->
+ <exclude-pattern>phpstan-stubs/*</exclude-pattern>
+
<!-- Skip the build directory -->
<exclude-pattern>build/*</exclude-pattern>
</ruleset>
diff --git a/packages/php/woocommerce-subscriptions-engine/phpstan-stubs/stubs.php b/packages/php/woocommerce-subscriptions-engine/phpstan-stubs/stubs.php
new file mode 100644
index 00000000000..363a90f3398
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/phpstan-stubs/stubs.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Stubs for PHPStan.
+ */
+
+namespace {
+ if ( ! class_exists( \PHPUnit_Framework_Exception::class ) ) {
+ /**
+ * Class needed by wordpress-stubs for PHPStan.
+ */
+ class PHPUnit_Framework_Exception {}
+ }
+}
+
+namespace WordPress\AiClient\Providers {
+ if ( ! class_exists( \WordPress\AiClient\Providers\AbstractProvider::class ) ) {
+ /**
+ * Class needed by wordpress-stubs for PHPStan.
+ */
+ abstract class AbstractProvider {
+ }
+ }
+}
+
+namespace WordPress\AiClient\Providers\DTO {
+ if ( ! class_exists( \WordPress\AiClient\Providers\DTO\ProviderMetadata::class ) ) {
+ /**
+ * Class needed by wordpress-stubs for PHPStan.
+ */
+ class ProviderMetadata {
+ }
+ }
+}
+
+namespace WordPress\AiClient\Providers\Models\DTO {
+ if ( ! class_exists( \WordPress\AiClient\Providers\Models\DTO\ModelMetadata::class ) ) {
+ /**
+ * Class needed by wordpress-stubs for PHPStan.
+ */
+ class ModelMetadata {
+ }
+ }
+}
+
+namespace WordPress\AiClient\Providers\Contracts {
+
+ if ( ! interface_exists( \WordPress\AiClient\Providers\Contracts\ProviderAvailabilityInterface::class ) ) {
+ /**
+ * Interface needed by wordpress-stubs for PHPStan.
+ */
+ interface ProviderAvailabilityInterface {
+ public function isConfigured(): bool;
+ }
+ }
+
+ if ( ! interface_exists( \WordPress\AiClient\Providers\Contracts\ModelMetadataDirectoryInterface::class ) ) {
+ /**
+ * Interface needed by wordpress-stubs for PHPStan.
+ */
+ interface ModelMetadataDirectoryInterface {
+ public function listModelMetadata(): array;
+ public function hasModelMetadata( string $modelId ): bool;
+ public function getModelMetadata( string $modelId ): \WordPress\AiClient\Providers\Models\DTO\ModelMetadata;
+ }
+ }
+ if ( ! interface_exists( \WordPress\AiClient\Providers\Contracts\ModelInterface::class ) ) {
+ /**
+ * Interface needed by wordpress-stubs for PHPStan.
+ */
+ interface ModelInterface {
+ }
+ }
+}
diff --git a/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.json b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.json
new file mode 100644
index 00000000000..f033df2291f
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.json
@@ -0,0 +1,20 @@
+{
+ "name": "automattic/woocommerce-subscriptions-engine-phpstan",
+ "description": "PHPStan analysis tasks for the WooCommerce Subscriptions engine.",
+ "type": "project",
+ "require": {
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2.2",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "php-stubs/woocommerce-stubs": "^10.8",
+ "php-stubs/wordpress-tests-stubs": "^7.0",
+ "szepeviktor/phpstan-wordpress": "^2.0"
+ },
+ "config": {
+ "platform": {
+ "php": "7.4"
+ }
+ }
+}
diff --git a/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.lock b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.lock
new file mode 100644
index 00000000000..1e8a48041a7
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/composer.lock
@@ -0,0 +1,343 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "132ae9762f20fa948d2b8dddd4c2118d",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "php-stubs/woocommerce-stubs",
+ "version": "v10.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-stubs/woocommerce-stubs.git",
+ "reference": "9d59e65dcabc18153c243cb9acd85fc88ef40589"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/9d59e65dcabc18153c243cb9acd85fc88ef40589",
+ "reference": "9d59e65dcabc18153c243cb9acd85fc88ef40589",
+ "shasum": ""
+ },
+ "require": {
+ "php-stubs/wordpress-stubs": "^5.3 || ^6.0"
+ },
+ "require-dev": {
+ "php": "~7.1 || ~8.0",
+ "php-stubs/generator": "^0.8.0"
+ },
+ "suggest": {
+ "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WooCommerce function and class declaration stubs for static analysis.",
+ "homepage": "https://github.com/php-stubs/woocommerce-stubs",
+ "keywords": [
+ "PHPStan",
+ "static analysis",
+ "woocommerce",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/php-stubs/woocommerce-stubs/issues",
+ "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v10.8.0"
+ },
+ "time": "2026-05-27T11:46:50+00:00"
+ },
+ {
+ "name": "php-stubs/wordpress-stubs",
+ "version": "v6.9.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-stubs/wordpress-stubs.git",
+ "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5",
+ "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5",
+ "shasum": ""
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "5.6.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "nikic/php-parser": "^5.5",
+ "php": "^7.4 || ^8.0",
+ "php-stubs/generator": "^0.8.6",
+ "phpdocumentor/reflection-docblock": "^6.0",
+ "phpstan/phpstan": "^2.1",
+ "phpunit/phpunit": "^9.5",
+ "symfony/polyfill-php80": "*",
+ "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1",
+ "wp-coding-standards/wpcs": "3.1.0 as 2.3.0"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Pure PHP implementation of libsodium",
+ "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WordPress function and class declaration stubs for static analysis.",
+ "homepage": "https://github.com/php-stubs/wordpress-stubs",
+ "keywords": [
+ "PHPStan",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/php-stubs/wordpress-stubs/issues",
+ "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4"
+ },
+ "time": "2026-05-01T20:36:01+00:00"
+ },
+ {
+ "name": "php-stubs/wordpress-tests-stubs",
+ "version": "v7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-stubs/wordpress-tests-stubs.git",
+ "reference": "5745979d58aebec7e7e659e9ffaf0a1b94785442"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-stubs/wordpress-tests-stubs/zipball/5745979d58aebec7e7e659e9ffaf0a1b94785442",
+ "reference": "5745979d58aebec7e7e659e9ffaf0a1b94785442",
+ "shasum": ""
+ },
+ "require-dev": {
+ "php": "^7.3 || ^8.0",
+ "php-stubs/generator": "^0.8.0"
+ },
+ "suggest": {
+ "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WordPress Tests function and class declaration stubs for static analysis.",
+ "homepage": "https://github.com/php-stubs/wordpress-tests-stubs",
+ "keywords": [
+ "PHPStan",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/php-stubs/wordpress-tests-stubs/issues",
+ "source": "https://github.com/php-stubs/wordpress-tests-stubs/tree/v7.0.0"
+ },
+ "time": "2026-05-25T08:19:01+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.2.2",
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
+ "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ondřej Mirtes"
+ },
+ {
+ "name": "Markus Staab"
+ },
+ {
+ "name": "Vincent Langlet"
+ }
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2026-06-05T09:00:01+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-phpunit",
+ "version": "2.0.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-phpunit.git",
+ "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32",
+ "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.32"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<7.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^5",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPUnit extensions and rules for PHPStan",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16"
+ },
+ "time": "2026-02-14T09:05:21+00:00"
+ },
+ {
+ "name": "szepeviktor/phpstan-wordpress",
+ "version": "v2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/szepeviktor/phpstan-wordpress.git",
+ "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/aa722f037b2d034828cd6c55ebe9e5c74961927e",
+ "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "php-stubs/wordpress-stubs": "^6.6.2",
+ "phpstan/phpstan": "^2.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.1.14",
+ "composer/semver": "^3.4",
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.1",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.0",
+ "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.0",
+ "wp-coding-standards/wpcs": "3.1.0 as 2.3.0"
+ },
+ "suggest": {
+ "swissspidy/phpstan-no-private": "Detect usage of internal core functions, classes and methods"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SzepeViktor\\PHPStan\\WordPress\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "WordPress extensions for PHPStan",
+ "keywords": [
+ "PHPStan",
+ "code analyse",
+ "code analysis",
+ "static analysis",
+ "wordpress"
+ ],
+ "support": {
+ "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues",
+ "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.3"
+ },
+ "time": "2025-09-14T02:58:22+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.4"
+ },
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "7.4"
+ },
+ "plugin-api-version": "2.6.0"
+}
diff --git a/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan-7.neon b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan-7.neon
new file mode 100644
index 00000000000..67e5351b31b
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan-7.neon
@@ -0,0 +1,7 @@
+# Use the main phpstan config
+includes:
+ - phpstan.neon
+
+# Rewrite tested PHP version
+parameters:
+ phpVersion: 70400
diff --git a/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan.neon b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan.neon
new file mode 100644
index 00000000000..10bdcf8e73b
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/phpstan.neon
@@ -0,0 +1,28 @@
+parameters:
+ level: 9
+ phpVersion: 80400
+ tmpDir: temp
+ bootstrapFiles:
+ - ../../vendor/autoload.php
+ - vendor/autoload.php
+ - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php
+ - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php
+ - ../../phpstan-stubs/stubs.php
+ - vendor/php-stubs/wordpress-tests-stubs/wordpress-tests-stubs.php
+ scanDirectories:
+ - ../../phpstan-stubs
+ paths:
+ - ../../src
+ - ../../tests
+ treatPhpDocTypesAsCertain: false
+
+ # Test bootstraps require the WP test framework at runtime, which PHPStan
+ # must not follow; exclude them from analysis (mirrors the email-editor task).
+ excludePaths:
+ analyseAndScan:
+ - ../../tests/unit/bootstrap.php
+ - ../../tests/integration/bootstrap.php
+
+includes:
+ - vendor/szepeviktor/phpstan-wordpress/extension.neon
+ - vendor/phpstan/phpstan-phpunit/extension.neon
diff --git a/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/run-phpstan.sh b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/run-phpstan.sh
new file mode 100755
index 00000000000..aef6efedaee
--- /dev/null
+++ b/packages/php/woocommerce-subscriptions-engine/tasks/phpstan/run-phpstan.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+set -e # Exit immediately on error
+
+cd "$(dirname "$0")" # Ensure we're in the script directory
+
+CLEANUP=true
+for arg in "$@"; do
+ if [ "$arg" = "--skip-cleanup" ]; then
+ CLEANUP=false
+ break
+ fi
+done
+
+cleanup() {
+ if [ "$CLEANUP" = true ]; then
+ echo "🧹 Cleaning up PHPStan directories."
+ rm -rf vendor/ temp/
+ fi
+}
+trap cleanup EXIT
+
+# Ensure composer is installed
+if ! command -v composer >/dev/null 2>&1; then
+ echo "❌ Composer is not installed. Please install Composer first."
+ exit 1
+fi
+
+# Determine which PHPStan config file to use
+CONFIG_FILE="phpstan.neon"
+for arg in "$@"; do
+ if [ "$arg" = "php7" ]; then
+ CONFIG_FILE="phpstan-7.neon"
+ break
+ fi
+done
+
+# Check if phpstan is available via composer
+if ! ./vendor/bin/phpstan --version >/dev/null 2>&1; then
+ echo "🔧 PHPStan not found. Installing dependencies..."
+ composer install --quiet
+fi
+
+# Run PHPStan
+echo "▶️ Running PHPStan with config: $CONFIG_FILE"
+vendor/bin/phpstan analyse -c "$CONFIG_FILE" --memory-limit=2G
+
+echo "✅ PHPStan completed successfully."