Passed
Push — master ( f8f76e...e5015c )
by Bas
12:29
created

NormalizesExpressions::normalizePredicate()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 17
c 1
b 0
f 1
nc 9
nop 1
dl 0
loc 29
ccs 14
cts 15
cp 0.9333
crap 6.0106
rs 9.0777
1
<?php
2
3
namespace LaravelFreelancerNL\FluentAQL\Traits;
4
5
use LaravelFreelancerNL\FluentAQL\Exceptions\ExpressionTypeException;
6
use LaravelFreelancerNL\FluentAQL\Expressions\BindExpression;
7
use LaravelFreelancerNL\FluentAQL\Expressions\Expression;
8
use LaravelFreelancerNL\FluentAQL\Expressions\ListExpression;
9
use LaravelFreelancerNL\FluentAQL\Expressions\NullExpression;
10
use LaravelFreelancerNL\FluentAQL\Expressions\ObjectExpression;
11
use LaravelFreelancerNL\FluentAQL\Expressions\PredicateExpression;
12
use LaravelFreelancerNL\FluentAQL\Expressions\QueryExpression;
13
use LaravelFreelancerNL\FluentAQL\Expressions\StringExpression;
14
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
15
16
trait NormalizesExpressions
17
{
18
19
    abstract public function bind($data, $to = null);
20
21
    /**
22
     * @param $argument
23
     * @param  array|null  $allowedExpressionTypes
24
     * @return Expression
25
     * @throws ExpressionTypeException
26
     */
27 6
    public function normalizeArgument($argument, $allowedExpressionTypes = null): Expression|NullExpression
28
    {
29 6
        if ($argument instanceof Expression) {
30 5
            return $argument;
31
        }
32
33 6
        if (is_scalar($argument)) {
34 6
            return $this->normalizeScalar($argument, $allowedExpressionTypes);
35
        }
36
37 5
        if (is_null($argument)) {
38
            return new NullExpression();
39
        }
40
41 5
        return $this->normalizeCompound($argument, $allowedExpressionTypes);
42
    }
43
44
    /**
45
     * @param $argument
46
     * @param $allowedExpressionTypes
47
     *
48
     * @throws ExpressionTypeException
49
     *
50
     * @return BindExpression
51
     */
52 6
    protected function normalizeScalar($argument, $allowedExpressionTypes)
53
    {
54 6
        $argumentType = $this->determineArgumentType($argument, $allowedExpressionTypes);
55
56 6
        return $this->createExpression($argument, $argumentType);
57
    }
58
59 6
    protected function createExpression($argument, $argumentType)
60
    {
61 6
        $expressionType = $this->grammar->mapArgumentTypeToExpressionType($argumentType);
62 6
        if ($expressionType == 'Bind') {
63 1
            return $this->bind($argument);
64
        }
65 6
        if ($expressionType == 'CollectionBind') {
66
            return $this->bindCollection($argument);
0 ignored issues
show
Bug introduced by
It seems like bindCollection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
            return $this->/** @scrutinizer ignore-call */ bindCollection($argument);
Loading history...
67
        }
68 6
        $expressionClass = '\LaravelFreelancerNL\FluentAQL\Expressions\\' . $expressionType . 'Expression';
69
70 6
        return new $expressionClass($argument);
71
    }
72
73 5
    protected function normalizeCompound($argument, $allowedExpressionTypes = null)
74
    {
75 5
        if (is_array($argument)) {
76 3
            return $this->normalizeArray($argument, $allowedExpressionTypes);
77
        }
78 5
        if (!is_iterable($argument)) {
79 5
            return $this->normalizeObject($argument, $allowedExpressionTypes);
80
        }
81
82
        return new ObjectExpression($this->normalizeIterable($argument, $allowedExpressionTypes));
83
    }
84
85
    /**
86
     * @param  array<mixed>|object  $argument
87
     * @param  array<mixed>|null  $allowedExpressionTypes
88
     *
89
     * @return array<mixed>|object
90
     * @throws ExpressionTypeException
91
     *
92
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
93
     */
94 3
    protected function normalizeIterable($argument, $allowedExpressionTypes = null): object|array
95
    {
96 3
        foreach ($argument as $attribute => $value) {
97 3
            $argument[$attribute] = $this->normalizeArgument($value);
98
        }
99
100 3
        return $argument;
101
    }
102
103
    /**
104
     * @param array<mixed>|PredicateExpression $predicates
105
     *
106
     * @return array|object
107
     */
108 4
    public function normalizePredicates(array|PredicateExpression $predicates)
109
    {
110 4
        if ($this->grammar->isPredicate($predicates)) {
111 4
            return $this->normalizePredicate($predicates);
112
        }
113
114 4
        $normalizedPredicates = [];
115 4
        foreach ($predicates as $predicate) {
116 4
            $normalizedPredicates[] = $this->normalizePredicates($predicate);
117
        }
118
119 4
        return $normalizedPredicates;
120
    }
121
122
    /**
123
     * @param non-empty-array<mixed>|PredicateExpression $predicate
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-array<mixed>|PredicateExpression at position 0 could not be parsed: Unknown type name 'non-empty-array' at position 0 in non-empty-array<mixed>|PredicateExpression.
Loading history...
124
     * @return PredicateExpression
125
     * @throws ExpressionTypeException
126
     */
127 4
    protected function normalizePredicate(array|PredicateExpression $predicate): PredicateExpression
128
    {
129 4
        if ($predicate instanceof PredicateExpression) {
0 ignored issues
show
introduced by
$predicate is never a sub-type of LaravelFreelancerNL\Flue...ons\PredicateExpression.
Loading history...
130 2
            return $predicate;
131
        }
132
133 4
        $leftOperand = $this->normalizeArgument($predicate[0]);
134
135 4
        $comparisonOperator = null;
136 4
        if (isset($predicate[1])) {
137 4
            $comparisonOperator = $predicate[1];
138
        }
139
140
141 4
        $rightOperand = null;
142 4
        if (isset($predicate[2])) {
143 4
            $rightOperand = $this->normalizeArgument($predicate[2]);
144
        }
145
146 4
        $logicalOperator = 'AND';
147 4
        if (isset($predicate[3]) && $this->grammar->isLogicalOperator($predicate[3])) {
148
            $logicalOperator = $predicate[3];
149
        }
150
151 4
        return new PredicateExpression(
152 4
            $leftOperand,
153
            $comparisonOperator,
154
            $rightOperand,
155
            $logicalOperator
156
        );
157
    }
158
159
    /**
160
     * Return the first matching expression type for the argument from the allowed types.
161
     *
162
     * @param string|iterable $argument
163
     * @param $allowedExpressionTypes
164
     *
165
     * @throws ExpressionTypeException
166
     *
167
     * @return mixed
168
     */
169 6
    protected function determineArgumentType($argument, $allowedExpressionTypes = null)
170
    {
171 6
        if (is_string($allowedExpressionTypes)) {
172 6
            $allowedExpressionTypes = [$allowedExpressionTypes];
173
        }
174 6
        if ($allowedExpressionTypes == null) {
175 5
            $allowedExpressionTypes = $this->grammar->getAllowedExpressionTypes();
176
        }
177
178 6
        foreach ($allowedExpressionTypes as $allowedExpressionType) {
179 6
            $check = 'is' . $allowedExpressionType;
180 6
            if ($allowedExpressionType == 'Reference' || $allowedExpressionType == 'RegisteredVariable') {
181 5
                if ($this->grammar->$check($argument, $this->variables)) {
182 5
                    return $allowedExpressionType;
183
                }
184
            }
185
186 6
            if ($this->grammar->$check($argument)) {
187 6
                return $allowedExpressionType;
188
            }
189
        }
190
191
        throw new ExpressionTypeException(
192
            "This argument, 
193
            '{$argument}', does not match one of these expression types: "
194
                . implode(', ', $allowedExpressionTypes)
195
                . '.'
196
        );
197
    }
198
199
    /**
200
     * @param $argument
201
     * @param $allowedExpressionTypes
202
     *
203
     * @return ListExpression|ObjectExpression
204
     * @throws ExpressionTypeException
205
     */
206 3
    protected function normalizeArray($argument, $allowedExpressionTypes)
207
    {
208 3
        if ($this->grammar->isAssociativeArray($argument)) {
209 2
            return new ObjectExpression($this->normalizeIterable($argument, $allowedExpressionTypes));
210
        }
211
212 1
        return new ListExpression($this->normalizeIterable($argument, $allowedExpressionTypes));
0 ignored issues
show
Bug introduced by
It seems like $this->normalizeIterable...allowedExpressionTypes) can also be of type object; however, parameter $expression of LaravelFreelancerNL\Flue...pression::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

212
        return new ListExpression(/** @scrutinizer ignore-type */ $this->normalizeIterable($argument, $allowedExpressionTypes));
Loading history...
213
    }
214
215
    /**
216
     * @param $argument
217
     * @param $allowedExpressionTypes
218
     *
219
     * @return Expression
220
     * @throws ExpressionTypeException
221
     */
222 5
    protected function normalizeObject($argument, $allowedExpressionTypes)
223
    {
224 5
        if ($argument instanceof \DateTimeInterface) {
225
            return new StringExpression($argument->format(\DateTime::ATOM));
226
        }
227
228 5
        if ($argument instanceof QueryBuilder) {
229 5
            return new QueryExpression($argument);
230
        }
231
232
        return new ObjectExpression($this->normalizeIterable((array) $argument, $allowedExpressionTypes));
233
    }
234
}
235