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) { |
|
|
|
|
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
|
|
|
if ($offset && $length) { |
87
|
|
|
return PHP_EOL . "LIMIT {$length} OFFSET {$offset}"; |
88
|
5 |
|
} |
89
|
|
|
|
90
|
|
|
if ($length) { |
91
|
4 |
|
return PHP_EOL . "LIMIT {$length}"; |
92
|
3 |
|
} |
93
|
|
|
|
94
|
|
|
return ''; |
95
|
1 |
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Return count query |
99
|
|
|
* |
100
|
|
|
* @param string $query |
101
|
|
|
* |
102
|
|
|
* @return string |
103
|
|
|
* |
104
|
|
|
* @see https://github.com/pear/Pager/blob/master/examples/Pager_Wrapper.php |
105
|
|
|
* Taken from pear/pager and modified. |
106
|
|
|
* tested at https://github.com/pear/Pager/blob/80c0e31c8b94f913cfbdeccbe83b63822f42a2f8/tests/pager_wrapper_test.php#L19 |
107
|
|
|
* @codeCoverageIgnore |
108
|
|
|
*/ |
109
|
|
|
public function rewriteCountQuery($query) |
110
|
|
|
{ |
111
|
|
|
if (is_int(strpos(strtolower($query), 'union'))) { |
112
|
|
|
return ''; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
if (preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $query) || preg_match('/\s+GROUP\s+BY\s+/is', $query)) { |
116
|
|
|
return ''; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$openParenthesis = '(?:\()'; |
120
|
|
|
$closeParenthesis = '(?:\))'; |
121
|
|
|
$subQueryInSelect = $openParenthesis . '.*\bFROM\b.*' . $closeParenthesis; |
122
|
|
|
$pattern = '/(?:.*' . $subQueryInSelect . '.*)\bFROM\b\s+/Uims'; |
123
|
|
|
if (preg_match($pattern, $query)) { |
124
|
|
|
return ''; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$subQueryWithLimitOrder = $openParenthesis . '.*\b(LIMIT|ORDER)\b.*' . $closeParenthesis; |
128
|
|
|
$pattern = '/.*\bFROM\b.*(?:.*' . $subQueryWithLimitOrder . '.*).*/Uims'; |
129
|
|
|
if (preg_match($pattern, $query)) { |
130
|
|
|
return ''; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$queryCount = preg_replace('/(?:.*)\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $query, 1); |
134
|
|
|
[$queryCount] = preg_split('/\s+ORDER\s+BY\s+/is', (string) $queryCount); |
135
|
|
|
[$queryCount] = preg_split('/\bLIMIT\b/is', (string) $queryCount); |
136
|
|
|
|
137
|
|
|
return trim((string) $queryCount); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
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.