Skip to content

Commit dfac0f6

Browse files
author
Kirill Nesmeyanov
committed
Add "skip when" expressions support
1 parent 77df1c4 commit dfac0f6

File tree

5 files changed

+61
-11
lines changed

5 files changed

+61
-11
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"phpunit/phpunit": "^10.5|^11.0",
3232
"rector/rector": "^1.1",
3333
"symfony/cache": "^5.4|^6.0|^7.0",
34+
"symfony/expression-language": "^5.4|^6.0|^7.0",
3435
"symfony/property-access": "^5.4|^6.0|^7.0",
3536
"symfony/stopwatch": "^5.4|^6.0|^7.0",
3637
"symfony/var-dumper": "^5.4|^6.0|^7.0",

src/Mapping/Driver/AttributeDriver.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
namespace TypeLang\Mapper\Mapping\Driver;
66

7+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
8+
use Symfony\Component\ExpressionLanguage\ParsedExpression;
79
use TypeLang\Mapper\Exception\Definition\PropertyTypeNotFoundException;
810
use TypeLang\Mapper\Exception\Definition\TypeNotFoundException;
11+
use TypeLang\Mapper\Exception\Environment\ComposerPackageRequiredException;
912
use TypeLang\Mapper\Mapping\MapName;
1013
use TypeLang\Mapper\Mapping\MapType;
1114
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
@@ -16,6 +19,49 @@
1619

1720
final class AttributeDriver extends LoadableDriver
1821
{
22+
public function __construct(
23+
DriverInterface $delegate = new NullDriver(),
24+
private ?ExpressionLanguage $expression = null,
25+
) {
26+
parent::__construct($delegate);
27+
}
28+
29+
/**
30+
* @throws ComposerPackageRequiredException
31+
*/
32+
private function getExpressionLanguage(): ExpressionLanguage
33+
{
34+
return $this->expression ??= $this->createDefaultExpressionLanguage();
35+
}
36+
37+
/**
38+
* @throws ComposerPackageRequiredException
39+
*/
40+
private function createDefaultExpressionLanguage(): ExpressionLanguage
41+
{
42+
if (!\class_exists(ExpressionLanguage::class)) {
43+
throw ComposerPackageRequiredException::becausePackageNotInstalled(
44+
package: 'symfony/expression-language',
45+
purpose: 'expressions support',
46+
);
47+
}
48+
49+
return new ExpressionLanguage();
50+
}
51+
52+
/**
53+
* @param non-empty-string $expression
54+
* @param list<non-empty-string> $names
55+
*
56+
* @throws ComposerPackageRequiredException
57+
*/
58+
private function createExpression(string $expression, array $names): ParsedExpression
59+
{
60+
$parser = $this->getExpressionLanguage();
61+
62+
return $parser->parse($expression, $names);
63+
}
64+
1965
#[\Override]
2066
protected function load(
2167
\ReflectionClass $reflection,
@@ -55,9 +101,11 @@ protected function load(
55101
$attribute = $this->findPropertyAttribute($property, SkipWhen::class);
56102

57103
if ($attribute !== null) {
58-
$type = $this->createType($attribute->type, $property, $types, $parser);
104+
$expression = $this->createExpression($attribute->expr, [
105+
'this',
106+
]);
59107

60-
$metadata->setSkipCondition($type);
108+
$metadata->setSkipCondition($expression);
61109
}
62110
}
63111
}

src/Mapping/Metadata/PropertyMetadata.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace TypeLang\Mapper\Mapping\Metadata;
66

7+
use Symfony\Component\ExpressionLanguage\ParsedExpression;
78
use TypeLang\Mapper\Runtime\Context;
89
use TypeLang\Parser\Node\Stmt\NamedTypeNode;
910
use TypeLang\Parser\Node\Stmt\Shape\NamedFieldNode;
@@ -20,7 +21,7 @@ final class PropertyMetadata extends Metadata
2021

2122
private bool $hasDefaultValue = false;
2223

23-
private ?TypeMetadata $skipWhen = null;
24+
private ?ParsedExpression $skipWhen = null;
2425

2526
/**
2627
* @param non-empty-string $export
@@ -157,9 +158,9 @@ public function findTypeInfo(): ?TypeMetadata
157158
/**
158159
* @api
159160
*/
160-
public function setSkipCondition(TypeMetadata $type): void
161+
public function setSkipCondition(ParsedExpression $expression): void
161162
{
162-
$this->skipWhen = $type;
163+
$this->skipWhen = $expression;
163164
}
164165

165166
/**
@@ -179,14 +180,14 @@ public function hasSkipCondition(): bool
179180
}
180181

181182
/**
182-
* Note: The prefix "find" is used to indicate that the {@see TypeMetadata}
183+
* Note: The prefix "find" is used to indicate that the {@see ParsedExpression}
183184
* definition may be optional and method may return {@see null}.
184185
* The prefix "get" is used when the value is forced to be obtained
185186
* and should throw an exception if the type definition is missing.
186187
*
187188
* @api
188189
*/
189-
public function findSkipCondition(): ?TypeMetadata
190+
public function findSkipCondition(): ?ParsedExpression
190191
{
191192
return $this->skipWhen;
192193
}

src/Mapping/SkipWhen.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public function __construct(
1111
/**
1212
* @var non-empty-string
1313
*/
14-
public readonly string $type,
14+
public readonly string $expr,
1515
) {}
1616
}

src/Type/ObjectType/ObjectTypeNormalizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ protected function normalizeObject(object $object, Context $context): array
9696

9797
// Skip the property when condition is matched
9898
$skip = $meta->findSkipCondition();
99+
99100
if ($skip !== null) {
100-
$condition = $skip->getType();
101+
$nodes = $skip->getNodes();
101102

102-
// Skip when condition is matched
103-
if ($condition->match($fieldValue, $entrance)) {
103+
if ((bool) $nodes->evaluate([], ['this' => $object])) {
104104
continue;
105105
}
106106
}

0 commit comments

Comments
 (0)