Skip to content

Releases: boundwize/structarmed

Released: StructArmed 0.18.1

Choose a tag to compare

@samsonasik samsonasik released this 27 Sep 07:10
0.18.1
14520db

ci build PHPStan

What's Changed

  • perf: Skip garbage collection during analyse() by @samsonasik in #466
  • refactor: Reuse rule-returned RuleViolation in Analyser by @samsonasik in #467
  • fix: Add static to closures declaring a trait or enum that reads $this on AddStaticAnonymousFunctionVisitor by @samsonasik in #469

Full Changelog: 0.18.0...0.18.1

Released: StructArmed 0.18.0

Choose a tag to compare

@samsonasik samsonasik released this 24 Sep 03:53
0.18.0
f2728fe

ci build PHPStan

StructArmed 0.18.0 improves CI integration and expands YAGNI analysis to named functions.

This release introduces:

  • GitHub Actions annotations with the new --report=github;
  • path exclusions for Architecture::layer();
  • better control over the PHPUnit extension in CI;
  • Boundwize\StructArmed\Rule\UsedFunctionAwareRuleInterface and the new fixable MustBeUsedFunctionRule.

GitHub Actions Report

StructArmed can now report violations directly as GitHub Actions annotations:

vendor/bin/structarmed analyse --report=github

Each violation is emitted as a GitHub workflow error annotation, so violations appear directly on affected files and lines in pull requests.

StructArmed GitHub report annotations

The regular console report is appended after the annotations, so View details still opens a readable StructArmed report in the job log.

For example:

- name: Run StructArmed
  run: vendor/bin/structarmed analyse --report=github

The GitHub report intentionally exits with 0, allowing violations to be surfaced as annotations without failing the job itself.

Use the regular console or json report when violations should fail the job.

The github and json reports also disable the progress bar automatically.

Exclude Paths From Layers

Architecture::layer() now supports excluding one or more nested paths from a layer.

For example:

return Architecture::define()
    ->layer(
        'Logger',
        'src/Logger/',
        excludePath: 'src/Logger/Factory/',
    )
    ->layer('Factory', 'src/Logger/Factory/');

Files under src/Logger/Factory/ now belong to Factory without also belonging to Logger.

Multiple paths can be excluded:

->layer(
    'Logger',
    'src/Logger/',
    excludePath: [
        'src/Logger/Factory/',
        'src/Logger/Formatter/',
    ],
)

The exclusion only applies to that particular layer. A file may still resolve to another matching path layer, namespace-pattern layer, or the automatically registered Source layer.

This mirrors the existing exclusion support in layerPattern() and makes nested architecture boundaries easier to express without changing the directory structure.

YAGNI For Named Functions

The YAGNI preset can now detect unused named functions.

A new rule:

MustBeUsedFunctionRule

checks whether a function is called or otherwise referenced within the scanned code.

For example:

-function legacy_helper(): string
-{
-    return 'unused';
-}

The rule understands usages including:

  • regular function calls;
  • first-class callable references;
  • function-name strings such as 'App\helper';
  • calls from top-level code.

A function that only recursively calls itself still counts as unused.

MustBeUsedFunctionRule supports --fix, removing the unused function automatically. It can also clean up an empty surrounding if (! function_exists(...)) guard and remove the file when only boilerplate remains.

The rule is enabled automatically by:

Preset::YAGNI()

As with the other YAGNI rules, only usages inside the scanned paths are known. Functions intended as public extension points for external consumers can be excluded with skipRule() or skip paths.

UsedFunctionAwareRuleInterface

Custom function rules can opt into function-usage analysis through the new:

Boundwize\StructArmed\Rule\UsedFunctionAwareRuleInterface

It extends FunctionRuleInterface and makes the usage state available through:

$functionNode->isReferenced

For example:

final readonly class CustomRule implements UsedFunctionAwareRuleInterface
{
    public function appliesTo(FunctionNode $functionNode): bool
    {
        return $functionNode->isInLayer('Source');
    }

    public function evaluate(FunctionNode $functionNode): ?RuleViolation
    {
        if ($functionNode->isReferenced) {
            return null;
        }

        // ...
    }
}

Usage analysis is opt-in: StructArmed performs the additional work only when at least one active rule implements UsedFunctionAwareRuleInterface.

PHPUnit Extension Controls

The PHPUnit extension is now easier to control in CI.

Disable StructArmed For One PHPUnit Run

Set:

STRUCTARMED_DISABLED=1 vendor/bin/phpunit

StructArmed remains configured in phpunit.xml, but the extension skips architecture analysis for that run.

This is useful when StructArmed already runs as a dedicated CI step.

Disable Progress Output

PHPUnit's own --no-progress now also disables the StructArmed progress bar:

vendor/bin/phpunit --no-progress

To disable only StructArmed's progress output while preserving PHPUnit's progress display:

<extensions>
    <bootstrap class="Boundwize\StructArmed\PHPUnit\StructArmedExtension">
        <parameter name="progress" value="false"/>
    bootstrap>
extensions>

Dependency Update

StructArmed now requires:

"nikic/php-parser": "^5.9"

What's Changed

  • feat: Add GithubReport for Github output with --report=github in #445
  • Bump nikic/php-parser version to ^5.9 in #448
  • feat: Support excludePath in Architecture::layer() in #450
  • feat: Append console report to github report output and exit 0 in #458
  • feat: Add STRUCTARMED_DISABLED env to disable the PHPUnit extension in #459
  • feat: Allow disabling progress bar in PHPUnit extension in #460
  • feat: Add UsedFunctionAwareRuleInterface and MustBeUsedFunctionRule for YAGNI preset in #462
  • Handle top-level function usage in #463

Full Changelog: 0.17.12...0.18.0

What's Changed

  • feat: Add GithubReport for Github output with --report=github by @samsonasik in #445
  • Bump nikic/php-parser version to ^5.9 by @samsonasik in #448
  • feat: Support excludePath in Architecture::layer() by @samsonasik in #450
  • feat: Append console report to github report output and exit 0 by @samsonasik in #458
  • feat: Add STRUCTARMED_DISABLED env to disable the PHPUnit extension by @samsonasik in #459
  • feat: Allow disabling progress bar in PHPUnit extension: StructArmedExtension via progress config or PHPUnit --no-progress by @samsonasik in #460
  • feat: Add UsedFunctionAwareRuleInterface and MustBeUsedFunctionRule for YAGNI preset by @samsonasik in #462
  • handle top level function usage by @samsonasik in #463
  • Prepare for 0.18.0 by @samsonasik in #447

Full Changelog: 0.17.12...0.18.0

Released: StructArmed 0.17.12

Choose a tag to compare

@samsonasik samsonasik released this 20 Sep 13:17
0.17.12
05a625d

ci build PHPStan

What's Changed

  • perf: Merge same layer check into allowed layers loop by @samsonasik in #455
  • doc: Clarify dependency layer resolution comments by @samsonasik in #457

Full Changelog: 0.17.11...0.17.12

Released: StructArmed 0.17.11

Choose a tag to compare

@samsonasik samsonasik released this 20 Sep 06:24
0.17.11
b3db6f8

ci build PHPStan

What's Changed

  • Fix enum protected-to-private fixer producing invalid "final private" members by @samsonasik in #453
  • fix: Keep newline after line comment when removing anonymous class parentheses by @samsonasik in #454

Full Changelog: 0.17.10...0.17.11

Released: StructArmed 0.17.10

Choose a tag to compare

@samsonasik samsonasik released this 18 Sep 13:24
0.17.10
c95b82c

ci build PHPStan

What's Changed

  • fix: Prefer specific layer over equally specific Source layer in NamespaceLayerResolver by @samsonasik in #449

Full Changelog: 0.17.9...0.17.10

Released: StructArmed 0.17.9

Choose a tag to compare

@samsonasik samsonasik released this 16 Sep 10:23
0.17.9
1e536cd

ci build PHPStan

What's Changed

  • refactor: Merge loadAnalysisNodesWithFileAnalysis into loadAnalysisNodes via $withFileAnalysis flag by @samsonasik in #440
  • perf: iterate pending workers directly during polling by @samsonasik in #441
  • perf: reuse collector's declared function names in FileAnalysisProvider by @samsonasik in #442
  • fix: Handle missing files gracefully in FileHashProvider by @samsonasik in #443

Full Changelog: 0.17.8...0.17.9

Released: StructArmed 0.17.8

Choose a tag to compare

@samsonasik samsonasik released this 15 Sep 15:05
0.17.8
052fdbe

ci build PHPStan

What's Changed

  • fix: Resolve same-file function calls only against unconditionally declared functions by @samsonasik in #439

Full Changelog: 0.17.7...0.17.8

Released: StructArmed 0.17.7

Choose a tag to compare

@samsonasik samsonasik released this 15 Sep 10:12
0.17.7
882e8da

ci build PHPStan

What's Changed

  • fix: Resolve names before PSR-1 file-state analysis, reusing the extractor's AST by @samsonasik in #438

Full Changelog: 0.17.6...0.17.7

Released: StructArmed 0.17.6

Choose a tag to compare

@samsonasik samsonasik released this 14 Sep 15:42
0.17.6
a306ac9

ci build PHPStan

What's Changed

  • perf: Optimize object-bound anonymous-function detection hot path by @samsonasik in #433
  • [docs] Update documentation about usage of ExtendedClassAwareRuleInterface by @samsonasik in #435
  • perf: Skip file path in analysis node cache metadata by @samsonasik in #436
  • fix: Resolve same-namespace function calls case-insensitively by @samsonasik in #437

Full Changelog: 0.17.5...0.17.6

Released: StructArmed 0.17.5

Choose a tag to compare

@samsonasik samsonasik released this 13 Sep 19:24
0.17.5
37bb9d7

ci build PHPStan

What's Changed

  • perf: Optimize baseline generation and filtering performance by @samsonasik in #431
  • fix: Prevent static conversion of object-bound anonymous functions by @samsonasik in #432

Full Changelog: 0.17.4...0.17.5