Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.14% covered (warning)
77.14%
27 / 35
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
OrderCompiler
77.14% covered (warning)
77.14%
27 / 35
50.00% covered (danger)
50.00%
2 / 4
16.34
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 compile
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
6.09
 localeIds
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
6.80
 parse
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Finder;
6
7use Cosray\Context;
8use Cosray\Exception\ParserException;
9
10final class OrderCompiler
11{
12    use CompilesField;
13
14    public function __construct(
15        private readonly array $builtins = [],
16        private readonly ?Context $context = null,
17    ) {}
18
19    public function compile(string $statement): string
20    {
21        if (trim($statement) === '') {
22            throw new ParserException('Empty order by clause');
23        }
24
25        $parsed = $this->parse($statement);
26
27        if (count($parsed) === 0) {
28            throw new ParserException('Invalid query');
29        }
30
31        $expressions = [];
32
33        foreach ($parsed as $field) {
34            $fieldName = $field['field'];
35            $expression = $this->builtins[$fieldName] ?? null;
36
37            if (!$expression) {
38                $expression = $this->compileField($fieldName, 'n.content', localeIds: $this->localeIds());
39            }
40
41            $expressions[] = $expression . ' ' . $field['direction'];
42        }
43
44        if (count($expressions) > 0) {
45            return "\n    " . implode(",\n    ", $expressions);
46        }
47
48        return '';
49    }
50
51    private function localeIds(): array
52    {
53        if (!$this->context) {
54            return ['zxx'];
55        }
56
57        $ids = [];
58        $locale = $this->context->locale();
59
60        while ($locale) {
61            $ids[] = $locale->id;
62            $locale = $locale->fallback();
63        }
64
65        return $ids;
66    }
67
68    private function parse(string $statement): array
69    {
70        $fields = explode(',', $statement);
71        $pattern = '/^\s*([a-zA-Z][a-zA-Z0-9._]*)\s*(asc|desc)?\s*$/i';
72        $result = [];
73
74        foreach ($fields as $field) {
75            if (preg_match($pattern, trim($field), $matches)) {
76                $result[] = [
77                    'field' => $matches[1],
78                    'direction' => strtoupper($matches[2] ?? null ?: 'ASC'),
79                ];
80            } else {
81                throw new ParserException('Invalid order by clause');
82            }
83        }
84
85        return $result;
86    }
87}