1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the Ray.AuraSqlModule package. |
4
|
|
|
* |
5
|
|
|
* @license http://opensource.org/licenses/MIT MIT |
6
|
|
|
*/ |
7
|
|
|
namespace Ray\AuraSqlModule\Pagerfanta; |
8
|
|
|
|
9
|
|
|
use Aura\Sql\ExtendedPdo; |
10
|
|
|
use Aura\Sql\ExtendedPdoInterface; |
11
|
|
|
use Pagerfanta\Adapter\AdapterInterface; |
12
|
|
|
|
13
|
|
|
class ExtendedPdoAdapter implements AdapterInterface |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var ExtendedPdoInterface |
17
|
|
|
*/ |
18
|
|
|
private $pdo; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var string |
22
|
|
|
*/ |
23
|
|
|
private $sql; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var array |
27
|
|
|
*/ |
28
|
|
|
private $params; |
29
|
|
|
|
30
|
14 |
|
public function __construct(ExtendedPdoInterface $pdo, string $sql, array $params) |
31
|
|
|
{ |
32
|
14 |
|
$this->pdo = $pdo; |
33
|
14 |
|
$this->sql = $sql; |
34
|
14 |
|
$this->params = $params; |
35
|
14 |
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* {@inheritdoc} |
39
|
|
|
*/ |
40
|
9 |
|
public function getNbResults() |
41
|
|
|
{ |
42
|
|
|
// be smart and try to guess the total number of records |
43
|
9 |
|
$countQuery = $this->rewriteCountQuery($this->sql); |
44
|
9 |
|
if (! $countQuery) { |
45
|
|
|
// GROUP BY => fetch the whole result set and count the rows returned |
46
|
1 |
|
$result = $this->pdo->query($this->sql)->fetchAll(); |
47
|
1 |
|
|
48
|
|
|
return ! $result ? 0 : \count($result); |
49
|
1 |
|
} |
50
|
|
|
if ($this->params) { |
|
|
|
|
51
|
8 |
|
$count = $this->pdo->fetchValue($countQuery, $this->params); |
52
|
|
|
|
53
|
2 |
|
return ! $count ? 0 : (int) $count; |
54
|
2 |
|
} |
55
|
2 |
|
$count = $this->pdo->fetchValue($countQuery); |
56
|
2 |
|
|
57
|
|
|
return ! $count ? 0 : (int) $count; |
58
|
2 |
|
} |
59
|
|
|
|
60
|
6 |
|
/** |
61
|
|
|
* {@inheritdoc} |
62
|
6 |
|
*/ |
63
|
|
|
public function getSlice($offset, $length) |
64
|
|
|
{ |
65
|
|
|
$sql = $this->sql . $this->getLimitClause($offset, $length); |
66
|
|
|
$result = $this->pdo->perform($sql, $this->params)->fetchAll(\PDO::FETCH_ASSOC); |
67
|
|
|
|
68
|
6 |
|
return ! $result ? [] : $result; |
69
|
|
|
} |
70
|
6 |
|
|
71
|
6 |
|
/** |
72
|
|
|
* {@inheritdoc} |
73
|
6 |
|
*/ |
74
|
|
|
public function getLimitClause($offset, $length) |
75
|
|
|
{ |
76
|
|
|
$hasLimit = $offset || $length; |
77
|
|
|
if ($offset && $length) { |
78
|
|
|
$clause = PHP_EOL . "LIMIT {$length}"; |
79
|
9 |
|
if ($offset) { |
80
|
|
|
$clause .= " OFFSET {$offset}"; |
81
|
9 |
|
} |
82
|
9 |
|
|
83
|
5 |
|
return $clause; |
84
|
5 |
|
} |
85
|
5 |
|
|
86
|
|
|
if ($hasLimit && $length) { |
87
|
|
|
return PHP_EOL . "LIMIT {$length}"; |
88
|
5 |
|
} |
89
|
|
|
|
90
|
|
|
return ''; |
91
|
4 |
|
} |
92
|
3 |
|
|
93
|
|
|
/** |
94
|
|
|
* Return count query |
95
|
1 |
|
* |
96
|
|
|
* @param string $query |
97
|
|
|
* |
98
|
|
|
* @return string |
99
|
|
|
* |
100
|
|
|
* @see https://github.com/pear/Pager/blob/master/examples/Pager_Wrapper.php |
101
|
|
|
* Taken from pear/pager and modified. |
102
|
|
|
* tested at https://github.com/pear/Pager/blob/80c0e31c8b94f913cfbdeccbe83b63822f42a2f8/tests/pager_wrapper_test.php#L19 |
103
|
|
|
* @codeCoverageIgnore |
104
|
|
|
*/ |
105
|
|
|
public function rewriteCountQuery($query) |
106
|
|
|
{ |
107
|
|
|
if (\is_int(\strpos(\strtolower($query), 'union'))) { |
108
|
|
|
return ''; |
109
|
|
|
} |
110
|
|
|
if (\preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $query) || \preg_match('/\s+GROUP\s+BY\s+/is', $query)) { |
111
|
|
|
return ''; |
112
|
|
|
} |
113
|
|
|
$openParenthesis = '(?:\()'; |
114
|
|
|
$closeParenthesis = '(?:\))'; |
115
|
|
|
$subQueryInSelect = $openParenthesis . '.*\bFROM\b.*' . $closeParenthesis; |
116
|
|
|
$pattern = '/(?:.*' . $subQueryInSelect . '.*)\bFROM\b\s+/Uims'; |
117
|
|
|
if (\preg_match($pattern, $query)) { |
118
|
|
|
return ''; |
119
|
|
|
} |
120
|
|
|
$subQueryWithLimitOrder = $openParenthesis . '.*\b(LIMIT|ORDER)\b.*' . $closeParenthesis; |
121
|
|
|
$pattern = '/.*\bFROM\b.*(?:.*' . $subQueryWithLimitOrder . '.*).*/Uims'; |
122
|
|
|
if (\preg_match($pattern, $query)) { |
123
|
|
|
return ''; |
124
|
|
|
} |
125
|
|
|
$queryCount = \preg_replace('/(?:.*)\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $query, 1); |
126
|
|
|
list($queryCount) = \preg_split('/\s+ORDER\s+BY\s+/is', (string) $queryCount); |
127
|
|
|
list($queryCount) = \preg_split('/\bLIMIT\b/is', (string) $queryCount); |
128
|
|
|
|
129
|
|
|
return \trim((string) $queryCount); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
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.