Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
58 / 60
95.00% covered (success)
95.00%
19 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
ClosureExpressionBuilder
96.67% covered (success)
96.67%
58 / 60
95.00% covered (success)
95.00%
19 / 20
34
0.00% covered (danger)
0.00%
0 / 1
 getObjectFieldValue
90.91% covered (success)
90.91%
20 / 22
0.00% covered (danger)
0.00%
0 / 1
9.06
 getSupportedTokenType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parameter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 string
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNull
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 eq
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 neq
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 gt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 gte
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 lt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 lte
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 in
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 notIn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 contains
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 notContains
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 andX
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 nandX
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 orX
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 norX
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 xorX
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Symftony\Xpression\Expr;
6
7use Symftony\Xpression\Lexer;
8
9class ClosureExpressionBuilder implements ExpressionBuilderInterface
10{
11    public static function getObjectFieldValue(mixed $object, mixed $field): mixed
12    {
13        if (\is_array($object)) {
14            return $object[$field];
15        }
16
17        $accessors = ['get', 'is'];
18
19        foreach ($accessors as $accessor) {
20            $accessor .= $field;
21
22            if (!method_exists($object, $accessor)) {
23                continue;
24            }
25
26            return $object->{$accessor}();
27        }
28
29        // __call should be triggered for get.
30        $accessor = $accessors[0].$field;
31
32        if (method_exists($object, '__call')) {
33            return $object->{$accessor}();
34        }
35
36        if ($object instanceof \ArrayAccess) {
37            return $object[$field];
38        }
39
40        if (isset($object->{$field})) {
41            return $object->{$field};
42        }
43
44        // camelcase field name to support different variable naming conventions
45        $ccField = preg_replace_callback('/_(.?)/', static fn ($matches) => strtoupper($matches[1]), $field);
46
47        foreach ($accessors as $accessor) {
48            $accessor .= $ccField;
49
50            if (!method_exists($object, $accessor)) {
51                continue;
52            }
53
54            return $object->{$accessor}();
55        }
56
57        return $object->{$field};
58    }
59
60    public function getSupportedTokenType(): int
61    {
62        return Lexer::T_ALL;
63    }
64
65    /**
66     * @param bool  $isValue
67     * @param mixed $value
68     */
69    public function parameter($value, $isValue = false): mixed
70    {
71        return $value;
72    }
73
74    public function string(mixed $value): mixed
75    {
76        return $value;
77    }
78
79    public function isNull(string $field): mixed
80    {
81        return static fn ($object) => null === ClosureExpressionBuilder::getObjectFieldValue($object, $field);
82    }
83
84    public function eq(string $field, mixed $value): mixed
85    {
86        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) === $value;
87    }
88
89    public function neq(string $field, mixed $value): mixed
90    {
91        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) !== $value;
92    }
93
94    public function gt(string $field, mixed $value): mixed
95    {
96        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) > $value;
97    }
98
99    public function gte(string $field, mixed $value): mixed
100    {
101        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) >= $value;
102    }
103
104    public function lt(string $field, mixed $value): mixed
105    {
106        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) < $value;
107    }
108
109    public function lte(string $field, mixed $value): mixed
110    {
111        return static fn ($object) => ClosureExpressionBuilder::getObjectFieldValue($object, $field) <= $value;
112    }
113
114    public function in(string $field, array $values): mixed
115    {
116        return static fn ($object) => \in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values, true);
117    }
118
119    public function notIn(string $field, array $values): mixed
120    {
121        return static fn ($object) => !\in_array(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $values, true);
122    }
123
124    public function contains(string $field, mixed $value): mixed
125    {
126        return static fn ($object) => str_contains(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $value);
127    }
128
129    public function notContains(string $field, mixed $value): mixed
130    {
131        return static fn ($object) => !str_contains(ClosureExpressionBuilder::getObjectFieldValue($object, $field), $value);
132    }
133
134    public function andX(array $expressions): mixed
135    {
136        return static function ($object) use ($expressions) {
137            foreach ($expressions as $expression) {
138                if (!$expression($object)) {
139                    return false;
140                }
141            }
142
143            return true;
144        };
145    }
146
147    public function nandX(array $expressions): mixed
148    {
149        $self = $this;
150
151        return static fn ($object) => !$self->andX($expressions)($object);
152    }
153
154    public function orX(array $expressions): mixed
155    {
156        return static function ($object) use ($expressions) {
157            foreach ($expressions as $expression) {
158                if ($expression($object)) {
159                    return true;
160                }
161            }
162
163            return false;
164        };
165    }
166
167    public function norX(array $expressions): mixed
168    {
169        $self = $this;
170
171        return static fn ($object) => !$self->orX($expressions)($object);
172    }
173
174    public function xorX(array $expressions): mixed
175    {
176        return static function ($object) use ($expressions) {
177            $result = 0;
178            foreach ($expressions as $expression) {
179                if ($expression($object)) {
180                    ++$result;
181                }
182            }
183
184            $countExpressions = \count($expressions);
185
186            return 1 === $result | (2 < $countExpressions & $result === $countExpressions);
187        };
188    }
189}