QueryLocator::offsetGet()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 2
c 3
b 1
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
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
    }
24
25
    /**
26
     * {@inheritdoc}
27
     */
28
    public function get(string $queryName) : string
29
    {
30
        $sqlFile = sprintf(
31
            '%s/%s.sql',
32
            $this->sqlDir,
33
            $queryName
34
        );
35
36
        return trim($this->getFileContents($sqlFile));
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function getCountQuery(string $queryName) : string
43
    {
44
        return $this->rewriteCountQuery($this->get($queryName));
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    #[ReturnTypeWillChange]
51
    public function offsetExists($offset)
52
    {
53
        return (bool) $this->get($offset);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    #[ReturnTypeWillChange]
60
    public function offsetGet($offset)
61
    {
62
        return $this->get($offset);
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    #[\ReturnTypeWillChange]
69
    public function offsetSet($offset, $value)
70
    {
71
        throw new ReadOnlyException('not supported');
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    #[ReturnTypeWillChange]
78
    public function offsetUnset($offset)
79
    {
80
        throw new ReadOnlyException('not supported');
81
    }
82
83
    /**
84
     * Return count query
85
     *
86
     * @see https://github.com/pear/Pager/blob/master/examples/Pager_Wrapper.php
87
     * Taken from pear/pager and modified.
88
     * tested at https://github.com/pear/Pager/blob/80c0e31c8b94f913cfbdeccbe83b63822f42a2f8/tests/pager_wrapper_test.php#L19
89
     * @codeCoverageIgnore
90
     */
91
    private function rewriteCountQuery(string $sql) : string
92
    {
93
        if (preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $sql) || preg_match('/\s+GROUP\s+BY\s+/is', $sql)) {
94
            throw new CountQueryException($sql);
95
        }
96
        $openParenthesis = '(?:\()';
97
        $closeParenthesis = '(?:\))';
98
        $subQueryInSelect = $openParenthesis . '.*\bFROM\b.*' . $closeParenthesis;
99
        $pattern = '/(?:.*' . $subQueryInSelect . '.*)\bFROM\b\s+/Uims';
100
        if (preg_match($pattern, $sql)) {
101
            throw new CountQueryException($sql);
102
        }
103
        $subQueryWithLimitOrder = $openParenthesis . '.*\b(LIMIT|ORDER)\b.*' . $closeParenthesis;
104
        $pattern = '/.*\bFROM\b.*(?:.*' . $subQueryWithLimitOrder . '.*).*/Uims';
105
        if (preg_match($pattern, $sql)) {
106
            throw new CountQueryException($sql);
107
        }
108
        $queryCount = preg_replace('/(?:.*)\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $sql, 1);
109
        if (! is_string($queryCount)) {
0 ignored issues
show
introduced by
The condition is_string($queryCount) is always true.
Loading history...
110
            throw new CountQueryException($sql);
111
        }
112
        list($queryCount) = preg_split('/\s+ORDER\s+BY\s+/is', $queryCount);
113
        if (! is_string($queryCount)) {
0 ignored issues
show
introduced by
The condition is_string($queryCount) is always true.
Loading history...
114
            throw new CountQueryException($sql);
115
        }
116
        list($queryCount) = preg_split('/\bLIMIT\b/is', $queryCount);
117
        if (! is_string($queryCount)) {
0 ignored issues
show
introduced by
The condition is_string($queryCount) is always true.
Loading history...
118
            throw new CountQueryException($sql);
119
        }
120
121
        return trim($queryCount);
122
    }
123
124
    private function getFileContents(string $file) : string
125
    {
126
        if (! file_exists($file)) {
127
            throw new QueryFileNotFoundException($file);
128
        }
129
        $contents = file_get_contents($file);
130
        if (! $contents) {
131
            throw new QueryFileNotFoundException($file);
132
        }
133
134
        return $contents;
135
    }
136
}
137