diff --git a/.gitattributes b/.gitattributes index efaa1f682..2acb5e51e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,5 +15,6 @@ /psalm.xml export-ignore /psalm-baseline.xml export-ignore /renovate.json export-ignore +/structarmed.php export-ignore /tools/ export-ignore /test/ export-ignore diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0ed531201..72bccb51a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -141,6 +141,41 @@ jobs: - name: "psalm" run: "tools/vendor/bin/psalm --shepherd --stats" + static-analysis-structarmed: + name: "Static Analysis by StructArmed" + + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + dependencies: + - "locked" + php-version: + - "8.4" + - "8.5" + operating-system: + - "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7 + + - name: "Install PHP" + uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # 2.37.2 + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + extensions: intl, sodium + ini-values: memory_limit=-1 + + - name: "Install dependencies" + uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # 4.0.0 + with: + dependency-versions: "${{ matrix.dependencies }}" + + - name: "StructArmed" + run: "vendor/bin/structarmed analyze" + mutation-tests: name: "Mutation tests" diff --git a/composer.json b/composer.json index f3d754fb1..88c4fc931 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ ], "require-dev": { "phpbench/phpbench": "^1.7.0", - "phpunit/phpunit": "^13.4.3" + "phpunit/phpunit": "^13.4.3", + "boundwize/structarmed": "^0.19.4" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index f7c7c3b9e..7c0a337b3 100644 --- a/composer.lock +++ b/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": "a3ff4fe42b74f4060727201ae806a6f9", + "content-hash": "7445da69da60347e07a440ae877c55e2", "packages": [ { "name": "jetbrains/phpstorm-stubs", @@ -109,6 +109,153 @@ } ], "packages-dev": [ + { + "name": "boundwize/jsonrecast", + "version": "1.0.37", + "source": { + "type": "git", + "url": "https://github.com/boundwize/jsonrecast.git", + "reference": "d37e0d808cd5c0a8f6e5d6f36ea363202950e399" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/boundwize/jsonrecast/zipball/d37e0d808cd5c0a8f6e5d6f36ea363202950e399", + "reference": "d37e0d808cd5c0a8f6e5d6f36ea363202950e399", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "boundwize/pyrameter": "^0.5", + "boundwize/structarmed": "^0.19", + "laminas/laminas-coding-standard": "^3.1", + "phpstan/phpstan": "^2.2", + "phpunit/phpunit": "^11.5", + "rector/rector": "^2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Boundwize\\JsonRecast\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abdul Malik Ikhsan", + "email": "samsonasik@gmail.com", + "homepage": "http://samsonasik.wordpress.com", + "role": "Developer" + } + ], + "description": "Editable JSON AST with visitor traversal and formatting-preserving printing.", + "homepage": "https://github.com/boundwize/jsonrecast", + "keywords": [ + "ast", + "formatting", + "json", + "parser", + "refactoring" + ], + "support": { + "issues": "https://github.com/boundwize/jsonrecast/issues", + "source": "https://github.com/boundwize/jsonrecast/tree/1.0.37" + }, + "funding": [ + { + "url": "https://github.com/samsonasik", + "type": "github" + } + ], + "time": "2026-10-10T08:53:11+00:00" + }, + { + "name": "boundwize/structarmed", + "version": "0.19.5", + "source": { + "type": "git", + "url": "https://github.com/boundwize/structarmed.git", + "reference": "457f6f55649e3dddc9aaaaa00bb29141addc6431" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/boundwize/structarmed/zipball/457f6f55649e3dddc9aaaaa00bb29141addc6431", + "reference": "457f6f55649e3dddc9aaaaa00bb29141addc6431", + "shasum": "" + }, + "require": { + "boundwize/jsonrecast": "^1.0", + "composer-runtime-api": "^2.0", + "fidry/cpu-core-counter": "^1.4", + "nikic/php-parser": "^5.9", + "php": "^8.2" + }, + "require-dev": { + "boundwize/pyrameter": "^0.5", + "laminas/laminas-coding-standard": "^3.1", + "phpstan/phpstan": "^2.2", + "phpunit/phpunit": "^11.5", + "rector/rector": "dev-main" + }, + "bin": [ + "bin/structarmed" + ], + "type": "library", + "autoload": { + "psr-4": { + "Boundwize\\StructArmed\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abdul Malik Ikhsan", + "email": "samsonasik@gmail.com", + "homepage": "http://samsonasik.wordpress.com", + "role": "Developer" + } + ], + "description": "Configurable PHP architecture guards — define your layers and rules, then keep them enforced", + "homepage": "https://github.com/boundwize/structarmed", + "keywords": [ + "Enforcement", + "analysis", + "architecture", + "ddd", + "dev", + "layer", + "mvc", + "psr1", + "psr12", + "psr15", + "psr4", + "static analysis", + "yagni" + ], + "support": { + "issues": "https://github.com/boundwize/structarmed/issues", + "source": "https://github.com/boundwize/structarmed/tree/0.19.5" + }, + "funding": [ + { + "url": "https://github.com/samsonasik", + "type": "github" + } + ], + "time": "2026-10-17T08:34:45+00:00" + }, { "name": "doctrine/annotations", "version": "2.0.2", @@ -261,6 +408,68 @@ ], "time": "2026-10-01T06:59:29+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "d384a7d0d6e67aa497327260ca95023e92dc2032" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/d384a7d0d6e67aa497327260ca95023e92dc2032", + "reference": "d384a7d0d6e67aa497327260ca95023e92dc2032", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0 || ^1.1.2", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "symfony/polyfill-mbstring": "^1.33", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.4.1" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2026-09-29T22:11:39+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.14.0", diff --git a/structarmed.php b/structarmed.php new file mode 100644 index 000000000..d16fb7e53 --- /dev/null +++ b/structarmed.php @@ -0,0 +1,39 @@ +skip([ + __DIR__ . '/test/unit/Fixture', + ]) + ->withPreset(Preset::PSR4()) + ->layer('Root', 'src/BetterReflection.php') + ->layer('Identifier', 'src/Identifier/') + ->layer('NodeCompiler', 'src/NodeCompiler/') + ->layer('Reflection', 'src/Reflection/') + ->layer('Reflector', 'src/Reflector/') + ->layer('SourceLocator', [ + 'src/SourceLocator/FileChecker.php', + 'src/SourceLocator/Exception/', + ]) + ->layer('SourceLocatorAst', 'src/SourceLocator/Ast/') + ->layer('Located', 'src/SourceLocator/Located/') + ->layer('SourceStubber', 'src/SourceLocator/SourceStubber/') + ->layer('SourceLocatorType', 'src/SourceLocator/Type/') + ->layer('Util', 'src/Util/') + ->ruleset([ + 'Root' => ['+SourceLocatorType'], + 'Identifier' => ['Reflection'], + 'NodeCompiler' => ['Reflection', 'Reflector', 'Util'], + 'Reflection' => ['+NodeCompiler', 'Located', 'Root', 'SourceLocatorType'], + 'Reflector' => ['+Identifier', 'SourceLocatorType'], + 'SourceLocator' => [], + 'SourceLocatorAst' => ['+Reflector', 'Located', 'Util'], + 'Located' => ['SourceLocator', 'Util'], + 'SourceStubber' => ['Reflection', 'SourceLocator', 'Util'], + 'SourceLocatorType' => ['+SourceLocatorAst', '+SourceStubber', 'Root'], + 'Util' => ['+SourceLocatorAst', 'SourceLocator'], + ]); diff --git a/test/unit/Reflection/Adapter/Exception/NotImplementedBecauseItTriggersAutoloadingTest.php b/test/unit/Reflection/Adapter/Exception/NotImplementedBecauseItTriggersAutoloadingTest.php index 86cc229a0..d78f266e6 100644 --- a/test/unit/Reflection/Adapter/Exception/NotImplementedBecauseItTriggersAutoloadingTest.php +++ b/test/unit/Reflection/Adapter/Exception/NotImplementedBecauseItTriggersAutoloadingTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflection\Adapter; +namespace Roave\BetterReflectionTest\Reflection\Adapter\Exception; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/test/unit/Reflection/Attribute/ReflectionAttributeHelperTest.php b/test/unit/Reflection/Attribute/ReflectionAttributeHelperTest.php index a0a1206df..31ea49ca8 100644 --- a/test/unit/Reflection/Attribute/ReflectionAttributeHelperTest.php +++ b/test/unit/Reflection/Attribute/ReflectionAttributeHelperTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflection; +namespace Roave\BetterReflectionTest\Reflection\Attribute; use PhpParser\Node; use PHPUnit\Framework\Attributes\CoversClass; diff --git a/test/unit/SourceLocator/Ast/Exception/ParseToAstFailureTest.php b/test/unit/SourceLocator/Ast/Exception/ParseToAstFailureTest.php index 3baf3fa4b..85d7d7285 100644 --- a/test/unit/SourceLocator/Ast/Exception/ParseToAstFailureTest.php +++ b/test/unit/SourceLocator/Ast/Exception/ParseToAstFailureTest.php @@ -2,13 +2,14 @@ declare(strict_types=1); -namespace Roave\BetterReflection\SourceLocator\Ast\Exception; +namespace Roave\BetterReflectionTest\SourceLocator\Ast\Exception; use Exception; use PhpParser\Error; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use ReflectionProperty; +use Roave\BetterReflection\SourceLocator\Ast\Exception\ParseToAstFailure; use Roave\BetterReflection\SourceLocator\Located\LocatedSource; #[CoversClass(ParseToAstFailure::class)] diff --git a/test/unit/SourceLocator/Ast/FindReflectionsInTreeTest.php b/test/unit/SourceLocator/Ast/FindReflectionsInTreeTest.php index 26b40e141..759d00119 100644 --- a/test/unit/SourceLocator/Ast/FindReflectionsInTreeTest.php +++ b/test/unit/SourceLocator/Ast/FindReflectionsInTreeTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflector; +namespace Roave\BetterReflectionTest\SourceLocator\Ast; use PhpParser\Node; use PHPUnit\Framework\Attributes\CoversClass; diff --git a/test/unit/SourceLocator/Ast/LocatorTest.php b/test/unit/SourceLocator/Ast/LocatorTest.php index fc5c05929..f40e6def4 100644 --- a/test/unit/SourceLocator/Ast/LocatorTest.php +++ b/test/unit/SourceLocator/Ast/LocatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflector; +namespace Roave\BetterReflectionTest\SourceLocator\Ast; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/test/unit/SourceLocator/Ast/Parser/MemoizingParserTest.php b/test/unit/SourceLocator/Ast/Parser/MemoizingParserTest.php index 283cd4617..aaf5a03ab 100644 --- a/test/unit/SourceLocator/Ast/Parser/MemoizingParserTest.php +++ b/test/unit/SourceLocator/Ast/Parser/MemoizingParserTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflector; +namespace Roave\BetterReflectionTest\SourceLocator\Ast\Parser; use PhpParser\Node; use PhpParser\Node\Name; diff --git a/test/unit/SourceLocator/Ast/Strategy/NodeToReflectionTest.php b/test/unit/SourceLocator/Ast/Strategy/NodeToReflectionTest.php index 590e0f3db..d184c5cbf 100644 --- a/test/unit/SourceLocator/Ast/Strategy/NodeToReflectionTest.php +++ b/test/unit/SourceLocator/Ast/Strategy/NodeToReflectionTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Roave\BetterReflectionTest\Reflector; +namespace Roave\BetterReflectionTest\SourceLocator\Ast\Strategy; use PhpParser\Node; use PhpParser\NodeTraverser;