-
-
Notifications
You must be signed in to change notification settings - Fork 424
Expand file tree
/
Copy pathQueryFormatter.php
More file actions
145 lines (124 loc) · 4.44 KB
/
Copy pathQueryFormatter.php
File metadata and controls
145 lines (124 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php
declare(strict_types=1);
namespace DebugBar\DataFormatter;
#[\AllowDynamicProperties]
class QueryFormatter extends DataFormatter
{
/**
* Removes extra spaces at the beginning and end of the SQL query and its lines.
*
*
*/
public function formatSql(string $sql): string
{
$sql = preg_replace("/\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?:\?)/", '?', $sql);
return trim(preg_replace("/\s*\n\s*/", "\n", $sql));
}
/**
* Check bindings for illegal (non UTF-8) strings, like Binary data.
*
*/
public function checkBindings(array $bindings): array
{
foreach ($bindings as &$binding) {
if (is_string($binding) && !mb_check_encoding($binding, 'UTF-8')) {
$binding = '[BINARY DATA]';
} elseif (is_array($binding)) {
$binding = '[' . implode(',', $this->checkBindings($binding)) . ']';
} elseif (is_resource($binding) || gettype($binding) === 'resource (closed)') {
$binding = '[RESOURCE]';
} elseif (is_object($binding)) {
if ($binding instanceof \Closure) {
$binding = '[CLOSURE]';
} else {
try {
$binding = json_encode(
$binding,
JSON_INVALID_UTF8_SUBSTITUTE
| JSON_UNESCAPED_UNICODE
| JSON_THROW_ON_ERROR
);
} catch (\JsonException $e) {
$binding = '[object ' . get_class($binding) . ']';
}
}
}
}
return $bindings;
}
/**
* Format a source object.
*
* @param object|null $source If the backtrace is disabled, the $source will be null.
*
*/
public function formatSource(?object $source, bool $short = false): string
{
if (! is_object($source)) {
return '';
}
$parts = [];
if (!$short && isset($source->namespace) && $source->namespace) {
$parts['namespace'] = $source->namespace . '::';
}
if (isset($source->name) && $source->name) {
$parts['name'] = $short ? basename($source->name) : $source->name;
} elseif (isset($source->file) && $source->file) {
$parts['name'] = basename($source->file);
} else {
return '';
}
$parts['line'] = ':' . ($source->line ?? 1);
return implode($parts);
}
/**
* Returns the SQL string with any parameters used embedded
*
*/
public function formatSqlWithBindings(string $sql, array $bindings, ?\PDO $pdo = null): string
{
foreach ($this->checkBindings($bindings) as $key => $binding) {
if (is_string($key) && str_starts_with($key, ':')) {
$key = substr($key, 1);
}
if ($binding === null) {
$binding = 'NULL';
} elseif (is_bool($binding)) {
$binding = $binding ? 'TRUE' : 'FALSE';
} elseif (! is_int($binding) && ! is_float($binding)) {
$binding = $this->quoteBinding($binding, $pdo);
}
// This regex matches placeholders only, not the question marks,
// nested in quotes, while we iterate through the bindings
// and substitute placeholders by suitable values.
$regex = is_numeric($key)
? "/(?<!\?)\?(?=(?:[^'\\\\']*'[^'\\\\']*')*[^'\\\\']*$)(?!\?)/"
: "/:{$key}(?![A-Za-z0-9_])(?=(?:[^'\\\\']*'[^'\\\\']*')*[^'\\\\']*$)/";
$sql = preg_replace($regex, addcslashes((string) $binding, '$'), $sql, is_numeric($key) ? 1 : -1);
}
return $sql;
}
protected function quoteBinding(string $binding, ?\PDO $pdo = null): string
{
try {
if ($pdo instanceof \PDO) {
return $pdo->quote($binding);
}
} catch (\PDOException $e) {
}
return $this->emulateQuote($binding);
}
protected function emulateQuote(string $value): string
{
$charMap = [
"\\" => "\\\\",
"\x00" => "\\0",
"\n" => "\\n",
"\r" => "\\r",
"'" => "\\'",
'"' => '\\"',
"\x1a" => "\\Z",
];
return "'" . strtr($value, $charMap) . "'";
}
}