Passed
Pull Request — 2.1 (#71)
by Vincent
14:21 queued 08:17
created

EntityAccessorParser   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 76
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 14
eloc 29
c 1
b 0
f 0
dl 0
loc 76
ccs 31
cts 31
cp 1
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A parseGetterCall() 0 23 5
A parsePropertyFetch() 0 15 3
A checkLeftOperandVariable() 0 4 2
A parse() 0 11 3
1
<?php
2
3
namespace Bdf\Prime\Query\Closure\Parser;
4
5
use PhpParser\Node\Expr;
6
use PhpParser\Node\Identifier;
7
use RuntimeException;
8
9
use function lcfirst;
10
use function str_starts_with;
11
use function substr;
12
13
/**
14
 * Parser for left operand of a comparison
15
 * The value must be extracted from the entity parameter
16
 *
17
 * Handles:
18
 * - $entity->property
19
 * - $entity->getter()
20
 * - $entity->subEntity->property
21
 */
22
final class EntityAccessorParser
23
{
24
    private string $parameterName;
25
26
    /**
27
     * @param string $parameterName Name of the entity parameter
28
     */
29 24
    public function __construct(string $parameterName)
30
    {
31 24
        $this->parameterName = $parameterName;
32
    }
33
34
    /**
35
     * @param Expr $expr Left operand expression
36
     * @return string The property path separated by dots. Ex: "property", "subEntity.property"
37
     */
38 22
    public function parse(Expr $expr): string
39
    {
40
        switch (true) {
41 22
            case $expr instanceof Expr\PropertyFetch:
42 18
                return $this->parsePropertyFetch($expr);
43
44 6
            case $expr instanceof Expr\MethodCall:
45 4
                return $this->parseGetterCall($expr);
46
47
            default:
48 2
                throw new RuntimeException('Invalid entity accessor ' . $expr->getType() . '. Only properties and getters can be used in filters.');
49
        }
50
    }
51
52 18
    private function parsePropertyFetch(Expr\PropertyFetch $propertyFetch): string
53
    {
54 18
        if (!$propertyFetch->name instanceof Identifier) {
55 1
            throw new RuntimeException('Dynamic property access is not allowed in filters');
56
        }
57
58 17
        $name = $propertyFetch->name->name;
59
60 17
        if ($propertyFetch->var instanceof Expr\Variable) {
61 17
            $this->checkLeftOperandVariable($propertyFetch->var);
62
63 16
            return $name;
64
        }
65
66 4
        return $this->parse($propertyFetch->var) . '.' . $name;
67
    }
68
69 4
    private function parseGetterCall(Expr\MethodCall $expr): string
70
    {
71 4
        if ($expr->args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $expr->args of type array<mixed,PhpParser\No...de\VariadicPlaceholder> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
72 1
            throw new RuntimeException('Only getters can be used in filters');
73
        }
74
75 3
        if (!$expr->name instanceof Identifier) {
76 1
            throw new RuntimeException('Dynamic method name is not allowed in filters');
77
        }
78
79 2
        $name = $expr->name->name;
80
81 2
        if (str_starts_with($name, 'get')) {
82 2
            $name = lcfirst(substr($name, 3));
83
        }
84
85 2
        if ($expr->var instanceof Expr\Variable) {
86 2
            $this->checkLeftOperandVariable($expr->var);
87
88 2
            return $name;
89
        }
90
91 2
        return $this->parse($expr->var) . '.' . $name;
92
    }
93
94 17
    private function checkLeftOperandVariable(Expr\Variable $variable): void
95
    {
96 17
        if ($variable->name !== $this->parameterName) {
97 1
            throw new RuntimeException('The left operand of a comparison must be a property or a getter of the entity.');
98
        }
99
    }
100
}
101