Passed
Branch 1.x (2176db)
by Akihito
11:17
created

QueryLocator::rewriteCountQuery()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 7
Bugs 1 Features 0
Metric Value
eloc 16
c 7
b 1
f 0
dl 0
loc 22
ccs 0
cts 0
cp 0
rs 9.4222
cc 5
nc 4
nop 1
crap 30
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Koriym\QueryLocator;
6
7
use Koriym\QueryLocator\Exception\CountQueryException;
8
use Koriym\QueryLocator\Exception\QueryFileNotFoundException;
9
use Koriym\QueryLocator\Exception\ReadOnlyException;
10
use ReturnTypeWillChange;
11
use function is_string;
12
13
final class QueryLocator implements QueryLocatorInterface
14
{
15
    /**
16
     * @var string
17
     */
18
    private $sqlDir;
19
20
    public function __construct(string $sqlDir)
21
    {
22
        $this->sqlDir = $sqlDir;
23 21
    }
24
25 21
    /**
26 21
     * {@inheritdoc}
27
     */
28
    public function get(string $queryName) : string
29
    {
30
        $sqlFile = sprintf(
31 17
            '%s/%s.sql',
32
            $this->sqlDir,
33 17
            $queryName
34 17
        );
35 17
36 17
        return trim($this->getFileContents($sqlFile));
37
    }
38 17
39 2
    /**
40
     * {@inheritdoc}
41 15
     */
42
    public function getCountQuery(string $queryName) : string
43 15
    {
44
        return $this->rewriteCountQuery($this->get($queryName));
45
    }
46
47
    /**
48
     * {@inheritdoc}
49 6
     */
50
    #[ReturnTypeWillChange]
51 6
    public function offsetExists($offset)
52
    {
53 4
        assert(is_string($offset));
54
55
        return (bool) $this->get($offset);
56
    }
57
58
    /**
59 1
     * {@inheritdoc}
60
     */
61 1
    #[ReturnTypeWillChange]
62
    public function offsetGet($offset)
63
    {
64
        assert(is_string($offset));
65
        return $this->get($offset);
66
    }
67 2
68
    /**
69 2
     * {@inheritdoc}
70
     */
71
    #[ReturnTypeWillChange]
72
    public function offsetSet($offset, $value)
73
    {
74
        throw new ReadOnlyException('not supported');
75 1
    }
76
77 1
    /**
78
     * {@inheritdoc}
79
     */
80
    #[ReturnTypeWillChange]
81
    public function offsetUnset($offset)
82
    {
83 1
        throw new ReadOnlyException('not supported');
84
    }
85 1
86
    /**
87
     * Return count query
88
     *
89
     * @see https://github.com/pear/Pager/blob/master/examples/Pager_Wrapper.php
90
     * Taken from pear/pager and modified.
91
     * tested at https://github.com/pear/Pager/blob/80c0e31c8b94f913cfbdeccbe83b63822f42a2f8/tests/pager_wrapper_test.php#L19
92
     * @codeCoverageIgnore
93
     */
94
    private function rewriteCountQuery(string $sql) : string
95
    {
96
        if (preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $sql) || preg_match('/\s+GROUP\s+BY\s+/is', $sql)) {
97
            throw new CountQueryException($sql);
98
        }
99
        $openParenthesis = '(?:\()';
100
        $closeParenthesis = '(?:\))';
101
        $subQueryInSelect = $openParenthesis . '.*\bFROM\b.*' . $closeParenthesis;
102
        $pattern = '/.*%na' . $subQueryInSelect . 'me.*\bFROM\b\s+/Uims';
103
        if (preg_match($pattern, $sql)) {
104
                throw new CountQueryException($sql);
105
        }
106
        $subQueryWithLimitOrder = $openParenthesis . '.*\b(LIMIT|ORDER)\b.*' . $closeParenthesis;
107
        $pattern = '/.*\bFROM\b.*.*%na' . $subQueryWithLimitOrder . 'me.*.*/Uims';
108
        if (preg_match($pattern, $sql)) {
109
            throw new CountQueryException($sql);
110
        }
111
        $queryCount = preg_replace('/.*\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $sql, 1);
112
        [$oderSplited] = preg_split('/\s+ORDER\s+BY\s+/is', $queryCount);
113
        [$limitSplited] = preg_split('/\bLIMIT\b/is', $oderSplited);
114
115
        return trim($limitSplited);
116
    }
117
118
    private function getFileContents(string $file) : string
119
    {
120
        if (! file_exists($file)) {
121
            throw new QueryFileNotFoundException($file);
122
        }
123
        $contents = file_get_contents($file);
124
125
        return $contents;
126
    }
127
}
128