Completed
Push — 1.x ( 28fe18...d32be4 )
by Akihito
17s queued 13s
created

ExtendedPdoAdapter   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 1
dl 0
loc 124
c 0
b 0
f 0
ccs 33
cts 33
cp 1
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B getNbResults() 0 21 6
A getSlice() 0 7 2
B getLimitClause() 0 18 7
B rewriteCountQuery() 0 30 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\AuraSqlModule\Pagerfanta;
6
7
use Aura\Sql\ExtendedPdoInterface;
8
use Pagerfanta\Adapter\AdapterInterface;
9
use PDO;
10
11
use function count;
12
use function is_int;
13
use function preg_match;
14
use function preg_replace;
15
use function preg_split;
16
use function strpos;
17
use function strtolower;
18
use function trim;
19
20
use const PHP_EOL;
21
22
class ExtendedPdoAdapter implements AdapterInterface
23
{
24
    /** @var ExtendedPdoInterface */
25
    private $pdo;
26
27
    /** @var string */
28
    private $sql;
29
30 14
    /** @var array<mixed> */
31
    private $params;
32 14
33 14
    /**
34 14
     * @param array<mixed> $params
35 14
     */
36
    public function __construct(ExtendedPdoInterface $pdo, string $sql, array $params)
37
    {
38
        $this->pdo = $pdo;
39
        $this->sql = $sql;
40 9
        $this->params = $params;
41
    }
42
43 9
    /**
44 9
     * {@inheritdoc}
45
     */
46 1
    public function getNbResults()
47 1
    {
48
        // be smart and try to guess the total number of records
49 1
        $countQuery = $this->rewriteCountQuery($this->sql);
50
        if (! $countQuery) {
51 8
            // GROUP BY => fetch the whole result set and count the rows returned
52
            $result = $this->pdo->perform($this->sql, $this->params)->fetchAll();
53 2
54 2
            return ! $result ? 0 : count($result);
55 2
        }
56 2
57
        if ($this->params) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->params of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
58 2
            $count = $this->pdo->fetchValue($countQuery, $this->params);
59
60 6
            return ! $count ? 0 : (int) $count;
61
        }
62 6
63
        $count = $this->pdo->fetchValue($countQuery);
64
65
        return ! $count ? 0 : (int) $count;
66
    }
67
68 6
    /**
69
     * {@inheritdoc}
70 6
     *
71 6
     * @return array<array>
72
     */
73 6
    public function getSlice($offset, $length)
74
    {
75
        $sql = $this->sql . $this->getLimitClause($offset, $length);
76
        $result = $this->pdo->perform($sql, $this->params)->fetchAll(PDO::FETCH_ASSOC);
77
78
        return ! $result ? [] : $result;
79 9
    }
80
81 9
    /**
82 9
     * {@inheritdoc}
83 5
     */
84 5
    public function getLimitClause(int $offset, int $length): string
85 5
    {
86
        $hasLimit = $offset || $length;
87
        if ($offset && $length) {
88 5
            $clause = PHP_EOL . "LIMIT {$length}";
89
            if ($offset) {
90
                $clause .= " OFFSET {$offset}";
91 4
            }
92 3
93
            return $clause;
94
        }
95 1
96
        if ($hasLimit && $length) {
97
            return PHP_EOL . "LIMIT {$length}";
98
        }
99
100
        return '';
101
    }
102
103
    /**
104
     * Return count query
105
     *
106
     * @param string $query
107
     *
108
     * @return string
109
     *
110
     * @see https://github.com/pear/Pager/blob/master/examples/Pager_Wrapper.php
111
     * Taken from pear/pager and modified.
112
     * tested at https://github.com/pear/Pager/blob/80c0e31c8b94f913cfbdeccbe83b63822f42a2f8/tests/pager_wrapper_test.php#L19
113
     * @codeCoverageIgnore
114
     */
115
    public function rewriteCountQuery($query)
116
    {
117
        if (is_int(strpos(strtolower($query), 'union'))) {
118
            return '';
119
        }
120
121
        if (preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $query) || preg_match('/\s+GROUP\s+BY\s+/is', $query)) {
122
            return '';
123
        }
124
125
        $openParenthesis = '(?:\()';
126
        $closeParenthesis = '(?:\))';
127
        $subQueryInSelect = $openParenthesis . '.*\bFROM\b.*' . $closeParenthesis;
128
        $pattern = '/(?:.*' . $subQueryInSelect . '.*)\bFROM\b\s+/Uims';
129
        if (preg_match($pattern, $query)) {
130
            return '';
131
        }
132
133
        $subQueryWithLimitOrder = $openParenthesis . '.*\b(LIMIT|ORDER)\b.*' . $closeParenthesis;
134
        $pattern = '/.*\bFROM\b.*(?:.*' . $subQueryWithLimitOrder . '.*).*/Uims';
135
        if (preg_match($pattern, $query)) {
136
            return '';
137
        }
138
139
        $queryCount = preg_replace('/(?:.*)\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $query, 1);
140
        [$queryCount] = preg_split('/\s+ORDER\s+BY\s+/is', (string) $queryCount);
141
        [$queryCount] = preg_split('/\bLIMIT\b/is', (string) $queryCount);
142
143
        return trim((string) $queryCount);
144
    }
145
}
146