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."