Skip to content

Commit 91a9d68

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 91a9d68

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

src/Expression/ForClasses/Andx.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
$this->expressions = $expressions;
22+
}
23+
24+
public function describe(ClassDescription $theClass, string $because): Description
25+
{
26+
$expressionsDescriptions = [];
27+
foreach ($this->expressions as $expression) {
28+
$expressionsDescriptions[] = $expression->describe($theClass, '')->toString();
29+
}
30+
return new Description(
31+
'all expressions must be true (' . implode(', ', $expressionsDescriptions) . ')',
32+
$because
33+
);
34+
}
35+
36+
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
37+
{
38+
foreach ($this->expressions as $expression) {
39+
$newViolations = new Violations();
40+
$expression->evaluate($theClass, $newViolations, $because);
41+
if (0 !== $newViolations->count()) {
42+
$violations->add(Violation::create(
43+
$theClass->getFQCN(),
44+
ViolationMessage::withDescription(
45+
$this->describe($theClass, $because),
46+
"The class '" . $theClass->getFQCN() . "' violated the expression "
47+
. $expression->describe($theClass, '')->toString()
48+
)
49+
));
50+
51+
return;
52+
}
53+
}
54+
}
55+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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_pass_the_rule_when_and_is_empty(): void
44+
{
45+
$interface = 'interface';
46+
$class = 'SomeClass';
47+
$classDescription = new ClassDescription(
48+
FullyQualifiedClassName::fromString('HappyIsland'),
49+
[],
50+
[FullyQualifiedClassName::fromString($interface)],
51+
FullyQualifiedClassName::fromString($class),
52+
false,
53+
false,
54+
false,
55+
false,
56+
false
57+
);
58+
$andConstraint = new Andx();
59+
60+
$because = 'reasons';
61+
$violations = new Violations();
62+
$andConstraint->evaluate($classDescription, $violations, $because);
63+
64+
self::assertEquals(0, $violations->count());
65+
}
66+
67+
public function test_it_should_not_pass_the_rule(): void
68+
{
69+
$interface = 'SomeInterface';
70+
$class = 'SomeClass';
71+
72+
$classDescription = new ClassDescription(
73+
FullyQualifiedClassName::fromString('HappyIsland'),
74+
[],
75+
[FullyQualifiedClassName::fromString($interface)],
76+
null,
77+
false,
78+
false,
79+
false,
80+
false,
81+
false
82+
);
83+
84+
$implementConstraint = new Implement($interface);
85+
$extendsConstraint = new Extend($class);
86+
$andConstraint = new Andx($implementConstraint, $extendsConstraint);
87+
88+
$because = 'reasons';
89+
$violationError = $andConstraint->describe($classDescription, $because)->toString();
90+
91+
$violations = new Violations();
92+
$andConstraint->evaluate($classDescription, $violations, $because);
93+
self::assertNotEquals(0, $violations->count());
94+
95+
$this->assertEquals(
96+
'all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons',
97+
$violationError
98+
);
99+
$this->assertEquals(
100+
"The class 'HappyIsland' violated the expression should extend SomeClass, but "
101+
. "all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons",
102+
$violations->get(0)->getError()
103+
);
104+
}
105+
}

0 commit comments

Comments
 (0)