Skip to content

Commit 6dd16f1

Browse files
author
Herberto Graca
committed
Create a And expression
This will allow for complex nested expressions, like ANDs inside ORs.
1 parent 976c200 commit 6dd16f1

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

src/Expression/ForClasses/Andx.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Expression\ForClasses;
6+
7+
use Arkitect\Analyzer\ClassDescription;
8+
use Arkitect\Expression\Description;
9+
use Arkitect\Expression\Expression;
10+
use Arkitect\Rules\Violation;
11+
use Arkitect\Rules\ViolationMessage;
12+
use Arkitect\Rules\Violations;
13+
14+
final class Andx implements Expression
15+
{
16+
/** @var Expression[] */
17+
private $expressions;
18+
19+
public function __construct(Expression ...$expressions)
20+
{
21+
if (\count($expressions) <= 1) {
22+
throw new \InvalidArgumentException('at least two expressions are required');
23+
}
24+
$this->expressions = $expressions;
25+
}
26+
27+
public function describe(ClassDescription $theClass, string $because): Description
28+
{
29+
$expressionsDescriptions = [];
30+
foreach ($this->expressions as $expression) {
31+
$expressionsDescriptions[] = $expression->describe($theClass, '')->toString();
32+
}
33+
return new Description(
34+
'all expressions must be true (' . implode(', ', $expressionsDescriptions) . ')',
35+
$because
36+
);
37+
}
38+
39+
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
40+
{
41+
foreach ($this->expressions as $expression) {
42+
$newViolations = new Violations();
43+
$expression->evaluate($theClass, $newViolations, $because);
44+
if (0 !== $newViolations->count()) {
45+
$violations->add(Violation::create(
46+
$theClass->getFQCN(),
47+
ViolationMessage::withDescription(
48+
$this->describe($theClass, $because),
49+
"The class '" . $theClass->getFQCN() . "' violated the expression "
50+
. $expression->describe($theClass, '')->toString()
51+
)
52+
));
53+
54+
return;
55+
}
56+
}
57+
}
58+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Tests\Unit\Expressions\ForClasses;
6+
7+
use Arkitect\Analyzer\ClassDescription;
8+
use Arkitect\Analyzer\FullyQualifiedClassName;
9+
use Arkitect\Expression\ForClasses\Andx;
10+
use Arkitect\Expression\ForClasses\Extend;
11+
use Arkitect\Expression\ForClasses\Implement;
12+
use Arkitect\Rules\Violations;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class AndxTest extends TestCase
16+
{
17+
public function test_it_should_pass_the_rule(): void
18+
{
19+
$interface = 'interface';
20+
$class = 'SomeClass';
21+
$classDescription = new ClassDescription(
22+
FullyQualifiedClassName::fromString('HappyIsland'),
23+
[],
24+
[FullyQualifiedClassName::fromString($interface)],
25+
FullyQualifiedClassName::fromString($class),
26+
false,
27+
false,
28+
false,
29+
false,
30+
false
31+
);
32+
$implementConstraint = new Implement($interface);
33+
$extendsConstraint = new Extend($class);
34+
$andConstraint = new Andx($implementConstraint, $extendsConstraint);
35+
36+
$because = 'reasons';
37+
$violations = new Violations();
38+
$andConstraint->evaluate($classDescription, $violations, $because);
39+
40+
self::assertEquals(0, $violations->count());
41+
}
42+
43+
public function test_it_should_not_pass_the_rule(): void
44+
{
45+
$interface = 'SomeInterface';
46+
$class = 'SomeClass';
47+
48+
$classDescription = new ClassDescription(
49+
FullyQualifiedClassName::fromString('HappyIsland'),
50+
[],
51+
[FullyQualifiedClassName::fromString($interface)],
52+
null,
53+
false,
54+
false,
55+
false,
56+
false,
57+
false
58+
);
59+
60+
$implementConstraint = new Implement($interface);
61+
$extendsConstraint = new Extend($class);
62+
$andConstraint = new Andx($implementConstraint, $extendsConstraint);
63+
64+
$because = 'reasons';
65+
$violationError = $andConstraint->describe($classDescription, $because)->toString();
66+
67+
$violations = new Violations();
68+
$andConstraint->evaluate($classDescription, $violations, $because);
69+
self::assertNotEquals(0, $violations->count());
70+
71+
$this->assertEquals(
72+
'all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons',
73+
$violationError
74+
);
75+
$this->assertEquals(
76+
"The class 'HappyIsland' violated the expression should extend SomeClass, but "
77+
. "all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons",
78+
$violations->get(0)->getError()
79+
);
80+
}
81+
}

0 commit comments

Comments
 (0)