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

FunctionCallParser::parse()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 18
ccs 9
cts 9
cp 1
rs 9.6111
cc 5
nc 4
nop 1
crap 5
1
<?php
2
3
namespace Bdf\Prime\Query\Closure\Parser;
4
5
use Bdf\Prime\Query\Closure\Filter\AtomicFilter;
6
use Bdf\Prime\Query\Closure\Value\LikeValue;
7
use PhpParser\Node\Arg;
8
use PhpParser\Node\Expr\FuncCall;
9
use PhpParser\Node\VariadicPlaceholder;
10
use RuntimeException;
11
12
/**
13
 * Parse predicate function calls
14
 *
15
 * Handle the following functions:
16
 * - str_contains : perform a LIKE %value% query
17
 * - str_starts_with : perform a LIKE value% query
18
 * - str_ends_with : perform a LIKE %value query
19
 * - in_array : perform an IN query
20
 */
21
final class FunctionCallParser
22
{
23
    private EntityAccessorParser $entityAccessorParser;
24
    private ValueParser $valueParser;
25
26
    /**
27
     * Map function name to parser
28
     *
29
     * @var array<string, callable(Arg[]):AtomicFilter>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, callable(Arg[]):AtomicFilter> at position 4 could not be parsed: Expected '>' at position 4, but found 'callable'.
Loading history...
30
     */
31
    private array $functions;
32
33 24
    public function __construct(EntityAccessorParser $entityAccessorParser, ValueParser $valueParser)
34
    {
35 24
        $this->entityAccessorParser = $entityAccessorParser;
36 24
        $this->valueParser = $valueParser;
37
38 24
        $this->functions = [
39 24
            'str_contains' => [$this, 'parseStrContains'],
40 24
            'str_starts_with' => [$this, 'parseStartsWith'],
41 24
            'str_ends_with' => [$this, 'parseEndsWith'],
42 24
            'in_array' => [$this, 'parseInArray'],
43 24
        ];
44
    }
45
46
    /**
47
     * Parse a predicate function call
48
     *
49
     * @param FuncCall $expr The function call node
50
     *
51
     * @return AtomicFilter
52
     */
53 8
    public function parse(FuncCall $expr): AtomicFilter
54
    {
55 8
        $function = $expr->name->toString();
0 ignored issues
show
Bug introduced by
The method toString() does not exist on PhpParser\Node\Expr. ( Ignorable by Annotation )

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

55
        /** @scrutinizer ignore-call */ 
56
        $function = $expr->name->toString();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
57 8
        if (!isset($this->functions[$function])) {
58 1
            throw new RuntimeException('Unsupported function call ' . $function . ' in filters. Supported functions are: ' . implode(', ', array_keys($this->functions)) . '.');
59
        }
60
61 7
        $args = $expr->args;
62
63 7
        foreach ($args as $arg) {
64 7
            if ($arg instanceof VariadicPlaceholder || $arg->unpack) {
65 1
                throw new RuntimeException('Unsupported unpacking in function call ' . $function . ' in filters.');
66
            }
67
        }
68
69
        /** @var Arg[] $args */
70 6
        return $this->functions[$function]($args);
71
    }
72
73 3
    private function parseStrContains(array $args): AtomicFilter
74
    {
75 3
        return new AtomicFilter(
76 3
            $this->entityAccessorParser->parse($args[0]->value),
77 3
            ':like',
78 3
            LikeValue::contains($this->valueParser->parse($args[1]->value)),
79 3
        );
80
    }
81
82 5
    private function parseStartsWith(array $args): AtomicFilter
83
    {
84 5
        return new AtomicFilter(
85 5
            $this->entityAccessorParser->parse($args[0]->value),
86 5
            ':like',
87 5
            LikeValue::startsWith($this->valueParser->parse($args[1]->value)),
88 5
        );
89
    }
90
91 3
    private function parseEndsWith(array $args): AtomicFilter
92
    {
93 3
        return new AtomicFilter(
94 3
            $this->entityAccessorParser->parse($args[0]->value),
95 3
            ':like',
96 3
            LikeValue::endsWith($this->valueParser->parse($args[1]->value)),
97 3
        );
98
    }
99
100 2
    private function parseInArray(array $args): AtomicFilter
101
    {
102 2
        return new AtomicFilter(
103 2
            $this->entityAccessorParser->parse($args[0]->value),
104 2
            ':in',
105 2
            $this->valueParser->parse($args[1]->value),
106 2
        );
107
    }
108
}
109