Repository navigation
Released: StructArmed 0.16.0
This release introduces 2 new interfaces:
Boundwize\StructArmed\Rule\UsedInterfaceAwareRuleInterfaceBoundwize\StructArmed\Rule\UsedTraitAwareRuleInterface
They complete the existing Boundwize\StructArmed\Rule\ExtendedClassAwareRuleInterface, which was introduced in 0.15.0.
These interfaces are used by the new YAGNI preset to detect abstractions that are not actually used by the scanned codebase.
The preset checks for:
- unused interfaces:
Boundwize\StructArmed\Rule\Rules\Class_\MustBeUsedInterfaceRule - unused abstract classes:
Boundwize\StructArmed\Rule\Rules\Class_\MustBeInterfaceRule - unused traits:
Boundwize\StructArmed\Rule\Rules\Class_\MustBeUsedTraitRule - concrete classes that are extended but never instantiated:
Boundwize\StructArmed\Rule\Rules\Class_\ExtendedClassMustBeAbstractOrInstantiatedRule
For the last case, the class can usually be made abstract.
-class SomeClassOnlyExtended
+abstract class SomeClassOnlyExtendedUsage detection also takes references into account, including type hints, instanceof, ::class, static calls, and class-name strings.
All YAGNI rules support --fix. Unused declarations can be removed, while classes that are only extended can be changed to abstract.
You can define YAGNI preset:
<?php
declare(strict_types=1);
use Boundwize\StructArmed\Architecture;
use Boundwize\StructArmed\Preset\Preset;
use Boundwize\StructArmed\Preset\Presets\Psr1Preset;
use Boundwize\StructArmed\Rule\Rules\Class_\MustBeFinalRule;
return Architecture::define()
// ... other config ...
- ->withPresets(Preset::PSR4(), Preset::PSR15());
+ ->withPresets(Preset::PSR4(), Preset::PSR15(), Preset::YAGNI());Using The New Interfaces In A Custom Rule
The new interfaces are marker interfaces that custom rules can implement instead of the plain Boundwize\StructArmed\Rule\RuleInterface (each marker extends it, so no other change is needed). Collecting usage information costs extra analysis time, so the analyser only computes it when an active rule declares that it needs it via one of these markers:
| Marker interface | Flags populated on ClassNode |
|---|---|
Boundwize\StructArmed\Rule\ExtendedClassAwareRuleInterface |
$isExtended, $isReferenced, $isInstantiated |
Boundwize\StructArmed\Rule\UsedInterfaceAwareRuleInterface |
$isImplemented, $isReferenced |
Boundwize\StructArmed\Rule\UsedTraitAwareRuleInterface |
$isReferenced |
Without a matching marker on at least one active rule, the corresponding flags keep their default false.
For example, a custom rule that requires every interface in a Contracts layer to actually be implemented or referenced:
<?php
namespace App\Architecture\Rules;
use Boundwize\StructArmed\Analyser\ClassNode;
use Boundwize\StructArmed\Rule\RuleViolation;
use Boundwize\StructArmed\Rule\UsedInterfaceAwareRuleInterface;
use function sprintf;
final readonly class ContractMustBeImplementedRule implements UsedInterfaceAwareRuleInterface
{
public function appliesTo(ClassNode $classNode): bool
{
return $classNode->isInterface
&& $classNode->isInLayer('Contracts');
}
public function evaluate(ClassNode $classNode): ?RuleViolation
{
if ($classNode->isImplemented || $classNode->isReferenced) {
return null;
}
return new RuleViolation(
message: sprintf('Contract [%s] must be implemented or referenced', $classNode->className),
file: $classNode->file,
line: $classNode->line,
className: $classNode->className,
layer: $classNode->layer,
);
}
}The built-in YAGNI preset rules follow this same pattern: MustBeUsedInterfaceRule implements UsedInterfaceAwareRuleInterface, MustBeUsedTraitRule implements UsedTraitAwareRuleInterface, and MustBeUsedAbstractClassRule and ExtendedClassMustBeAbstractOrInstantiatedRule implement ExtendedClassAwareRuleInterface.
See the Reading Usage Flags In A Custom Rule documentation for more details.
YAGNI only knows about usage inside the scanned paths. If a library intentionally exposes interfaces or extension points for external consumers, the relevant rules or paths should be skipped.
What's Changed
- feat: Add YAGNI preset by @samsonasik in #308
Full Changelog: 0.15.44...0.16.0