Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure layer internal tag #1318

Merged
29 changes: 0 additions & 29 deletions baseline.xml
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.13.1@086b94371304750d1c673315321a55d15fc59015">
<file src="src/Contract/Config/Collector/BoolConfig.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[array{
* must: array<array-key, array{private: bool, type: string}>|mixed,
* must_not: array<array-key, array{private: bool, type: string}>|mixed,
* private: bool,
* type: string}]]></code>
</ImplementedReturnTypeMismatch>
</file>
<file src="src/Contract/Config/Collector/ComposerConfig.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[array{
* composerPath: string,
* composerLockPath: string,
* packages: list<string>,
* private: bool,
* type: string}]]></code>
</ImplementedReturnTypeMismatch>
</file>
<file src="src/Contract/Config/Collector/SuperGlobalConfig.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[array{'private': bool, 'type': string, 'value': string[]}]]></code>
</ImplementedReturnTypeMismatch>
</file>
<file src="src/Contract/Config/ConfigurableCollectorConfig.php">
<ImplementedReturnTypeMismatch>
<code>array{private: bool, type: string, value: string}</code>
</ImplementedReturnTypeMismatch>
</file>
<file src="src/Contract/Result/OutputResult.php">
<InvalidReturnStatement>
<code><![CDATA[array_key_exists($type, $this->rules) ? array_values($this->rules[$type]) : []]]></code>
Expand Down
9 changes: 8 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
use Qossmic\Deptrac\Core\Layer\Collector\MethodCollector;
use Qossmic\Deptrac\Core\Layer\Collector\PhpInternalCollector;
use Qossmic\Deptrac\Core\Layer\Collector\SuperglobalCollector;
use Qossmic\Deptrac\Core\Layer\Collector\TagValueRegexCollector;
use Qossmic\Deptrac\Core\Layer\Collector\TraitCollector;
use Qossmic\Deptrac\Core\Layer\Collector\UsesCollector;
use Qossmic\Deptrac\Core\Layer\LayerResolver;
Expand Down Expand Up @@ -258,6 +259,9 @@
$services
->set(ClassNameRegexCollector::class)
->tag('collector', ['type' => CollectorType::TYPE_CLASS_NAME_REGEX->value]);
$services
->set(TagValueRegexCollector::class)
->tag('collector', ['type' => CollectorType::TYPE_TAG_VALUE_REGEX->value]);
$services
->set(DirectoryCollector::class)
->tag('collector', ['type' => CollectorType::TYPE_DIRECTORY->value]);
Expand Down Expand Up @@ -336,7 +340,10 @@
->tag('kernel.event_subscriber');
$services
->set(DependsOnInternalToken::class)
->tag('kernel.event_subscriber');
->tag('kernel.event_subscriber')
->args([
'$config' => param('analyser'),
]);
$services
->set(UnmatchedSkippedViolations::class)
->tag('kernel.event_subscriber');
Expand Down
19 changes: 12 additions & 7 deletions deptrac.config.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Internal\Qossmic\Deptrac\IgnoreDependenciesOnContract;
use Qossmic\Deptrac\Contract\Config\AnalyserConfig;
use Qossmic\Deptrac\Contract\Config\Collector\BoolConfig;
use Qossmic\Deptrac\Contract\Config\Collector\ComposerConfig;
use Qossmic\Deptrac\Contract\Config\Collector\DirectoryConfig;
Expand All @@ -18,13 +19,17 @@

$config
->paths('src')
->analysers(
EmitterType::CLASS_TOKEN,
EmitterType::CLASS_SUPERGLOBAL_TOKEN,
EmitterType::FILE_TOKEN,
EmitterType::FUNCTION_TOKEN,
EmitterType::FUNCTION_SUPERGLOBAL_TOKEN,
EmitterType::FUNCTION_CALL,
->analyser(
AnalyserConfig::create()
->internalTag( '@internal' )
->types(
EmitterType::CLASS_TOKEN,
EmitterType::CLASS_SUPERGLOBAL_TOKEN,
EmitterType::FILE_TOKEN,
EmitterType::FUNCTION_TOKEN,
EmitterType::FUNCTION_SUPERGLOBAL_TOKEN,
EmitterType::FUNCTION_CALL
)
)
->layers(
$analyser = Layer::withName('Analyser')->collectors(
Expand Down
1 change: 1 addition & 0 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ deptrac:
- ./src

analyser:
internal_tag: "@internal"
types:
- class
- class_superglobal
Expand Down
25 changes: 25 additions & 0 deletions docs/collectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ deptrac:
Every class name that matches the regular expression becomes a part of the
*controller* layer.

## `tagValueRegex` Collector

The `tagValueRegex` collector allows collecting classes and functions by
matching the name and value of any tag in their phpdoc block, such as @internal
or @deprecated.

Any matching class will be added to the assigned layer.

```yaml
deptrac:
layers:
- name: Deprecated
collectors:
- type: tagValueRegex
tag: '@deprecated'
- name: DeprecatedSinceV2
collectors:
- type: tagValueRegex
tag: '@deprecated'
value: '/^since v2/i'
```
All classes tagged with "@deprecated" become part of the
*Deprecated* layer. All classes that specify that they have been
deprecated since v2 also become part of the *DeprecatedSinceV2* layer.

## `composer` Collector

The `composer` collector allows you to define dependencies on composer `require` or `require-dev` packages that follow PSR-0 or PSR-4 autoloading convention. With this collector you can for example enforce:
Expand Down
17 changes: 17 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@ The following table shows the available config keys for Deptrac.
</thead>
<tbody>
<tr>
<td>analyser.internal_tag</td>
<td>
Specifies a custom doc block tag which deptrac should use to identify layer-internal
class-like structures. The tag <code>@deptrac-internal</code> will always be used
for this purpose. This option allows an additional tag to be specified, such as
<code>@layer-internal</code> or plain <code>@internal</code>.
</td>
<td>

```yaml
deptrac:
analyser:
internal_tag: "@layer-internal"
```
</td>
</tr>
<tr>
<td>analyser.types</td>
<td>

Expand Down
18 changes: 18 additions & 0 deletions src/Contract/Ast/TaggedTokenReferenceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Contract\Ast;

/**
* Represents the AST-Token, its location, and associated tags.
*/
interface TaggedTokenReferenceInterface extends TokenReferenceInterface
{
public function hasTag(string $name): bool;

/**
* @return ?list<string>
*/
public function getTagLines(string $name): ?array;
}
55 changes: 55 additions & 0 deletions src/Contract/Config/AnalyserConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Contract\Config;

final class AnalyserConfig
{
/** @var array<string, EmitterType> */
private array $types = [];

/** @var ?string */
private ?string $internalTag = null;

private function __construct() {}

/** @param ?array<array-key,EmitterType> $types */
public static function create(array $types = null, string $internalTag = null): self
{
$analyser = new self();

$types ??= [EmitterType::CLASS_TOKEN, EmitterType::FUNCTION_TOKEN];
$analyser->types(...$types);

$analyser->internalTag($internalTag);

return $analyser;
}

public function types(EmitterType ...$types): self
{
$this->types = [];
foreach ($types as $type) {
$this->types[$type->value] = $type;
}

return $this;
}

public function internalTag(?string $tag): self
{
$this->internalTag = $tag;

return $this;
}

/** @return array<string, mixed> */
public function toArray(): array
{
return [
'types' => array_map(static fn (EmitterType $emitterType) => $emitterType->value, $this->types),
'internal_tag' => $this->internalTag,
];
}
}
36 changes: 36 additions & 0 deletions src/Contract/Config/Collector/TagValueRegexConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Qossmic\Deptrac\Contract\Config\Collector;

use Qossmic\Deptrac\Contract\Config\CollectorConfig;
use Qossmic\Deptrac\Contract\Config\CollectorType;

final class TagValueRegexConfig extends CollectorConfig
{
protected CollectorType $collectorType = CollectorType::TYPE_TAG_VALUE_REGEX;

public function __construct(
private string $tag,
private ?string $value = null
) {}

public static function create(string $tag, string $regexpr = null): self
{
return new self($tag, $regexpr);
}

public function match(string $regexpr): self
{
$this->value = $regexpr;

return $this;
}

public function toArray(): array
{
return [
'tag' => $this->tag,
'value' => $this->value,
] + parent::toArray();
}
}
2 changes: 1 addition & 1 deletion src/Contract/Config/CollectorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
return $this;
}

/** @return array{'type': string, 'private': bool} */
/** @return array{'type': string, 'private': bool, ...} */
public function toArray(): array

Check warning on line 20 in src/Contract/Config/CollectorConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.1)

Escaped Mutant for Mutator "PublicVisibility": --- Original +++ New @@ @@ return $this; } /** @return array{'type': string, 'private': bool, ...} */ - public function toArray() : array + protected function toArray() : array { return ['type' => $this->collectorType->value, 'private' => $this->private]; } }

Check warning on line 20 in src/Contract/Config/CollectorConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.3)

Escaped Mutant for Mutator "PublicVisibility": --- Original +++ New @@ @@ return $this; } /** @return array{'type': string, 'private': bool, ...} */ - public function toArray() : array + protected function toArray() : array { return ['type' => $this->collectorType->value, 'private' => $this->private]; } }

Check warning on line 20 in src/Contract/Config/CollectorConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.2)

Escaped Mutant for Mutator "PublicVisibility": --- Original +++ New @@ @@ return $this; } /** @return array{'type': string, 'private': bool, ...} */ - public function toArray() : array + protected function toArray() : array { return ['type' => $this->collectorType->value, 'private' => $this->private]; } }
{
return [
'type' => $this->collectorType->value,
Expand Down
1 change: 1 addition & 0 deletions src/Contract/Config/CollectorType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum CollectorType: string
case TYPE_CLASS = 'class';
case TYPE_CLASSLIKE = 'classLike';
case TYPE_CLASS_NAME_REGEX = 'classNameRegex';
case TYPE_TAG_VALUE_REGEX = 'tagValueRegex';
case TYPE_DIRECTORY = 'directory';
case TYPE_EXTENDS = 'extends';
case TYPE_FUNCTION_NAME = 'functionName';
Expand Down
19 changes: 12 additions & 7 deletions src/Contract/Config/DeptracConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@
private array $formatters = [];
/** @var array<Ruleset> */
private array $rulesets = [];
/** @var array<string, EmitterType> */
private array $analyser = [];
private ?AnalyserConfig $analyser = null;
/** @var array<string, array<string>> */
private array $skipViolations = [];
/** @var array<string> */
private array $excludeFiles = [];

/**
* @deprecated use analyser(AnalyserConfig::create()) instead
*/
public function analysers(EmitterType ...$types): self
{
foreach ($types as $type) {
$this->analyser[$type->value] = $type;
}
return $this->analyser(AnalyserConfig::create($types));
}

public function analyser(AnalyserConfig $analyser): self
{
$this->analyser = $analyser;

return $this;
}
Expand Down Expand Up @@ -109,15 +114,15 @@
$config['paths'] = $this->paths;
}

if ([] !== $this->analyser) {
$config['analyser']['types'] = array_map(static fn (EmitterType $emitterType) => $emitterType->value, $this->analyser);
if ($this->analyser) {
$config['analyser'] = $this->analyser->toArray();
}

if ([] !== $this->formatters) {
$config['formatters'] = array_map(static fn (FormatterConfigInterface $formatterConfig) => $formatterConfig->toArray(), $this->formatters);
}

if ([] !== $this->excludeFiles) {

Check warning on line 125 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.1)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->formatters) { $config['formatters'] = array_map(static fn(FormatterConfigInterface $formatterConfig) => $formatterConfig->toArray(), $this->formatters); } - if ([] !== $this->excludeFiles) { + if ([] === $this->excludeFiles) { $config['exclude_files'] = $this->excludeFiles; } if ([] !== $this->layers) {

Check warning on line 125 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.3)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->formatters) { $config['formatters'] = array_map(static fn(FormatterConfigInterface $formatterConfig) => $formatterConfig->toArray(), $this->formatters); } - if ([] !== $this->excludeFiles) { + if ([] === $this->excludeFiles) { $config['exclude_files'] = $this->excludeFiles; } if ([] !== $this->layers) {

Check warning on line 125 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.2)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->formatters) { $config['formatters'] = array_map(static fn(FormatterConfigInterface $formatterConfig) => $formatterConfig->toArray(), $this->formatters); } - if ([] !== $this->excludeFiles) { + if ([] === $this->excludeFiles) { $config['exclude_files'] = $this->excludeFiles; } if ([] !== $this->layers) {
$config['exclude_files'] = $this->excludeFiles;
}

Expand All @@ -129,13 +134,13 @@
$config['ruleset'] = array_map(static fn (Ruleset $rulesetConfig) => $rulesetConfig->toArray(), $this->rulesets);
}

if ([] !== $this->skipViolations) {

Check warning on line 137 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.1)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->rulesets) { $config['ruleset'] = array_map(static fn(Ruleset $rulesetConfig) => $rulesetConfig->toArray(), $this->rulesets); } - if ([] !== $this->skipViolations) { + if ([] === $this->skipViolations) { $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses;

Check warning on line 137 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.3)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->rulesets) { $config['ruleset'] = array_map(static fn(Ruleset $rulesetConfig) => $rulesetConfig->toArray(), $this->rulesets); } - if ([] !== $this->skipViolations) { + if ([] === $this->skipViolations) { $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses;

Check warning on line 137 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.2)

Escaped Mutant for Mutator "NotIdentical": --- Original +++ New @@ @@ if ([] !== $this->rulesets) { $config['ruleset'] = array_map(static fn(Ruleset $rulesetConfig) => $rulesetConfig->toArray(), $this->rulesets); } - if ([] !== $this->skipViolations) { + if ([] === $this->skipViolations) { $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses;
$config['skip_violations'] = $this->skipViolations;
}

$config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses;

return $config;

Check warning on line 143 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.1)

Escaped Mutant for Mutator "ArrayOneItem": --- Original +++ New @@ @@ $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses; - return $config; + return count($config) > 1 ? array_slice($config, 0, 1, true) : $config; } public function getExtensionAlias() : string {

Check warning on line 143 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.3)

Escaped Mutant for Mutator "ArrayOneItem": --- Original +++ New @@ @@ $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses; - return $config; + return count($config) > 1 ? array_slice($config, 0, 1, true) : $config; } public function getExtensionAlias() : string {

Check warning on line 143 in src/Contract/Config/DeptracConfig.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.2)

Escaped Mutant for Mutator "ArrayOneItem": --- Original +++ New @@ @@ $config['skip_violations'] = $this->skipViolations; } $config['ignore_uncovered_internal_classes'] = $this->ignoreUncoveredInternalClasses; - return $config; + return count($config) > 1 ? array_slice($config, 0, 1, true) : $config; } public function getExtensionAlias() : string {
}

public function getExtensionAlias(): string
Expand Down
25 changes: 21 additions & 4 deletions src/Core/Analyser/EventHandler/DependsOnInternalToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@
*/
class DependsOnInternalToken implements ViolationCreatingInterface
{
public function __construct(private readonly EventHelper $eventHelper) {}
private ?string $internalTag;

/**
* @param array{internal_tag:string|null, ...} $config
*/
public function __construct(
private readonly EventHelper $eventHelper,
array $config)
{
$this->internalTag = $config['internal_tag'];
}

public static function getSubscribedEvents()
{
Expand All @@ -29,10 +39,17 @@
foreach ($event->dependentLayers as $dependentLayer => $_) {
if ($event->dependerLayer !== $dependentLayer
&& $event->dependentReference instanceof ClassLikeReference
&& $event->dependentReference->isInternal
) {
$this->eventHelper->addSkippableViolation($event, $ruleset, $dependentLayer, $this);
$event->stopPropagation();
$isInternal = $event->dependentReference->hasTag('@deptrac-internal');

if (!$isInternal && null !== $this->internalTag) {
$isInternal = $event->dependentReference->hasTag($this->internalTag);
}

if ($isInternal) {
$this->eventHelper->addSkippableViolation($event, $ruleset, $dependentLayer, $this);

Check warning on line 50 in src/Core/Analyser/EventHandler/DependsOnInternalToken.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.1)

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $isInternal = $event->dependentReference->hasTag($this->internalTag); } if ($isInternal) { - $this->eventHelper->addSkippableViolation($event, $ruleset, $dependentLayer, $this); + $event->stopPropagation(); } }

Check warning on line 50 in src/Core/Analyser/EventHandler/DependsOnInternalToken.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.3)

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $isInternal = $event->dependentReference->hasTag($this->internalTag); } if ($isInternal) { - $this->eventHelper->addSkippableViolation($event, $ruleset, $dependentLayer, $this); + $event->stopPropagation(); } }

Check warning on line 50 in src/Core/Analyser/EventHandler/DependsOnInternalToken.php

View workflow job for this annotation

GitHub Actions / infection (ubuntu-22.04, 8.2)

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $isInternal = $event->dependentReference->hasTag($this->internalTag); } if ($isInternal) { - $this->eventHelper->addSkippableViolation($event, $ruleset, $dependentLayer, $this); + $event->stopPropagation(); } }
$event->stopPropagation();
}
}
}
}
Expand Down
Loading
Loading