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

add selector modifiers #289

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions docs/documentation/selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,83 @@ Selector::NOT(
```

This will select all classes that are not in the `App\User` namespace.

## Selector::AllOf(...$selectors)

Selects classes that match all the inner Selectors. (and operator)

```php
Selector::AllOf(
Selector::namespace('App\User'),
Selector::isAbstract()
)
```

This will select all abstract classes in the `App\User` namespace.

## Selector::AnyOf(...$selectors)

Selects classes that match any of the inner Selectors. (or operator)

```php
Selector::AnyOf(
Selector::namespace('App\User'),
Selector::isAbstract()
)
```

This will select all classes in the `App\User` namespace and all abstract classes.

## Selector::NoneOf(...$selectors)

Selects classes that do not match any of the inner Selectors. (not of operator)

```php
Selector::NoneOf(
Selector::namespace('App\User'),
Selector::isAbstract()
)
```

This will select all classes that are not in the `App\User` namespace and are not abstract.

## Selector::OneOf(...$selectors)

Selects classes that match exactly one of the inner Selectors. (xor operator)

```php
Selector::OneOf(
Selector::namespace('App\User'),
Selector::isAbstract(),
Selector::isTrait()
)
```

This will select all classes that are in the `App\User` namespace or are abstract or are traits, but not more than one of them.


## Selector::AtLeastCountOf(int $min, ...$selectors)

Selects classes that match at least X of the inner Selectors. (at least x of operator)

```php
Selector::AtLeastCountOf(2,
Selector::namespace('App\User'),
Selector::isAbstract()
)
```

This will select all classes that are in the `App\User` namespace and are abstract.

## Selector::AtMostCountOf(int $max, ...$selectors)

Selects classes that match at most X of the inner Selectors. (at most x of operator)

```php
Selector::AtMostCountOf(1,
Selector::namespace('App\User'),
Selector::isAbstract()
)
```

This will select all classes that are in the `App\User` namespace or are abstract, but not both.
35 changes: 35 additions & 0 deletions src/Selector/Modifier/AllOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class AllOfSelectorModifier implements SelectorInterface
{
/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(SelectorInterface ...$selectors)
{
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \implode(' and ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors));
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
foreach ($this->selectors as $selector) {
if (!$selector->matches($classReflection)) {
return false;
}
}

return true;
}
}
35 changes: 35 additions & 0 deletions src/Selector/Modifier/AnyOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class AnyOfSelectorModifier implements SelectorInterface
{
/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(SelectorInterface ...$selectors)
{
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \implode(' or ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors));
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
foreach ($this->selectors as $selector) {
if ($selector->matches($classReflection)) {
return true;
}
}

return false;
}
}
47 changes: 47 additions & 0 deletions src/Selector/Modifier/AtLeastCountOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class AtLeastCountOfSelectorModifier implements SelectorInterface
{
private int $count;

/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(int $count, SelectorInterface ...$selectors)
{
$this->count = $count;
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \sprintf(
'at least %d of %s',
$this->count,
\implode(' and ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors)),
);
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
$matches = 0;
foreach ($this->selectors as $selector) {
if ($selector->matches($classReflection)) {
++$matches;
}

if ($matches >= $this->count) {
return true;
}
}

return $matches >= $this->count;
}
}
47 changes: 47 additions & 0 deletions src/Selector/Modifier/AtMostCountOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class AtMostCountOfSelectorModifier implements SelectorInterface
{
private int $count;

/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(int $count, SelectorInterface ...$selectors)
{
$this->count = $count;
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \sprintf(
'at most %d of %s',
$this->count,
\implode(' and ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors)),
);
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
$matches = 0;
foreach ($this->selectors as $selector) {
if ($selector->matches($classReflection)) {
++$matches;
}

if ($matches > $this->count) {
return false;
}
}

return $matches <= $this->count;
}
}
38 changes: 38 additions & 0 deletions src/Selector/Modifier/NoneOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class NoneOfSelectorModifier implements SelectorInterface
{
/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(SelectorInterface ...$selectors)
{
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \sprintf(
'none of: %s',
\implode(' and ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors)),
);
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
foreach ($this->selectors as $selector) {
if ($selector->matches($classReflection)) {
return false;
}
}

return true;
}
}
43 changes: 43 additions & 0 deletions src/Selector/Modifier/OneOfSelectorModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types=1);

namespace PHPat\Selector\Modifier;

use PHPat\Selector\SelectorInterface;
use PHPStan\Reflection\ClassReflection;

final class OneOfSelectorModifier implements SelectorInterface
{
/** @var array<SelectorInterface> */
private array $selectors;

public function __construct(SelectorInterface ...$selectors)
{
$this->selectors = $selectors;
}

#[\Override]
public function getName(): string
{
return \sprintf(
'one of: [ %s ]',
\implode(' and ', \array_map(static fn ($selector) => $selector->getName(), $this->selectors)),
);
}

#[\Override]
public function matches(ClassReflection $classReflection): bool
{
$matches = 0;
foreach ($this->selectors as $selector) {
if ($selector->matches($classReflection)) {
++$matches;
}

if ($matches > 1) {
return false;
}
}

return $matches === 1;
}
}
36 changes: 36 additions & 0 deletions src/Selector/Selector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

namespace PHPat\Selector;

use PHPat\Selector\Modifier\AllOfSelectorModifier;
use PHPat\Selector\Modifier\AndModifier;
use PHPat\Selector\Modifier\AnyOfSelectorModifier;
use PHPat\Selector\Modifier\AtLeastCountOfSelectorModifier;
use PHPat\Selector\Modifier\AtMostCountOfSelectorModifier;
use PHPat\Selector\Modifier\NoneOfSelectorModifier;
use PHPat\Selector\Modifier\NotModifier;
use PHPat\Selector\Modifier\OneOfSelectorModifier;

final class Selector extends SelectorPrimitive
{
Expand All @@ -16,4 +22,34 @@ public static function NOT(SelectorInterface $selector): NotModifier
{
return new NotModifier($selector);
}

public static function AllOf(SelectorInterface ...$selectors): AllOfSelectorModifier
{
return new AllOfSelectorModifier(...$selectors);
}

public static function AnyOf(SelectorInterface ...$selectors): AnyOfSelectorModifier
{
return new AnyOfSelectorModifier(...$selectors);
}

public static function NoneOf(SelectorInterface ...$selectors): NoneOfSelectorModifier
{
return new NoneOfSelectorModifier(...$selectors);
}

public static function AtLeastCountOf(int $count, SelectorInterface ...$selectors): AtLeastCountOfSelectorModifier
{
return new AtLeastCountOfSelectorModifier($count, ...$selectors);
}

public static function AtMostCountOf(int $count, SelectorInterface ...$selectors): AtMostCountOfSelectorModifier
{
return new AtMostCountOfSelectorModifier($count, ...$selectors);
}

public static function OneOf(SelectorInterface ...$selectors): OneOfSelectorModifier
{
return new OneOfSelectorModifier(...$selectors);
}
}
Loading