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