Completed
Pull Request — 4.1 (#107)
by David
03:26
created

OrderByAnalyzer::trimDirection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
namespace Mouf\Database\TDBM;
3
use Doctrine\Common\Cache\Cache;
4
use PHPSQLParser\PHPSQLParser;
5
6
/**
7
 * Class in charge of analyzing order by clauses.
8
 *
9
 * Analyzing those clauses are important because an order by clause must be pushed into the select clause too.
10
 */
11
class OrderByAnalyzer
12
{
13
    /**
14
     * The content of the cache variable.
15
     *
16
     * @var Cache
17
     */
18
    private $cache;
19
20
    /**
21
     * @var string
22
     */
23
    private $cachePrefix;
24
25
    /**
26
     * OrderByAnalyzer constructor.
27
     * @param Cache $cache
28
     * @param string|null $cachePrefix
29
     */
30
    public function __construct(Cache $cache, $cachePrefix = null)
31
    {
32
        $this->cache = $cache;
33
        $this->cachePrefix = $cachePrefix;
34
    }
35
36
    /**
37
     * Returns an array for each sorted "column" in the form:
38
     *
39
     * [
40
     *      [
41
     *          'type' => 'colref',
42
     *          'table' => null,
43
     *          'column' => 'a',
44
     *          'direction' => 'ASC'
45
     *      ],
46
     *      [
47
     *          'type' => 'expr',
48
     *          'expr' => 'RAND()',
49
     *          'direction' => 'DESC'
50
     *      ]
51
     * ]
52
     *
53
     * @param string $orderBy
54
     * @return array
55
     */
56
    public function analyzeOrderBy(string $orderBy) : array
57
    {
58
        $key = $this->cachePrefix.'_order_by_analysis_'.$orderBy;
59
        $results = $this->cache->fetch($key);
60
        if ($results !== false) {
61
            return $results;
62
        }
63
        $results = $this->analyzeOrderByNoCache($orderBy);
64
        $this->cache->save($key, $results);
65
        return $results;
66
    }
67
68
    private function analyzeOrderByNoCache(string $orderBy) : array
69
    {
70
        $sqlParser = new PHPSQLParser();
71
        $sql = 'SELECT 1 FROM a ORDER BY '.$orderBy;
72
        $parsed = $sqlParser->parse($sql, true);
73
74
        $results = [];
75
76
        for ($i = 0, $count = count($parsed['ORDER']); $i < $count; $i++) {
77
            $orderItem = $parsed['ORDER'][$i];
78
            if ($orderItem['expr_type'] === 'colref') {
79
                $parts = $orderItem['no_quotes']['parts'];
80
                $columnName = array_pop($parts);
81
                if (!empty($parts)) {
82
                    $tableName = array_pop($parts);
83
                } else {
84
                    $tableName = null;
85
                }
86
87
                $results[] = [
88
                    'type' => 'colref',
89
                    'table' => $tableName,
90
                    'column' => $columnName,
91
                    'direction' => $orderItem['direction']
92
                ];
93
            } else {
94
                $position = $orderItem['position'];
95
                if ($i+1 < $count) {
96
                    $nextPosition = $parsed['ORDER'][$i+1]['position'];
97
                    $str = substr($sql, $position, $nextPosition - $position);
98
                } else {
99
                    $str = substr($sql, $position);
100
                }
101
102
                $str = trim($str, " \t\r\n,");
103
104
                $results[] = [
105
                    'type' => 'expr',
106
                    'expr' => $this->trimDirection($str),
107
                    'direction' => $orderItem['direction']
108
                ];
109
            }
110
111
112
        }
113
114
        return $results;
115
    }
116
117
    /**
118
     * Trims the ASC/DESC direction at the end of the string.
119
     *
120
     * @param string $sql
121
     * @return string
122
     */
123
    private function trimDirection(string $sql) : string
124
    {
125
        preg_match('/^(.*)(\s+(DESC|ASC|))*$/Ui', $sql, $matches);
126
127
        return $matches[1];
128
    }
129
}
130