Skip to content

Commit 48f5328

Browse files
committed
fallback to manual escaping
1 parent 251ef7d commit 48f5328

File tree

1 file changed

+92
-5
lines changed

1 file changed

+92
-5
lines changed

src/QueryLogger/QueryLogger.php

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace RodrigoPedra\QueryLogger;
44

5-
use Illuminate\Database\ConnectionResolverInterface;
65
use Illuminate\Database\Events\QueryExecuted;
76
use Illuminate\Support\Arr;
87
use Psr\Log\LoggerInterface;
@@ -11,21 +10,109 @@
1110
{
1211
public function __construct(
1312
private LoggerInterface $logger,
14-
private ConnectionResolverInterface $db,
1513
) {}
1614

1715
public function handle(QueryExecuted $event): void
1816
{
19-
$this->logger->debug($event->toRawSql(), [
17+
$this->logger->debug($this->sql($event), [
2018
'time' => $event->time,
2119
'connection' => $event->connectionName,
22-
'database' => $this->db->connection($event->connectionName)->getDatabaseName(),
20+
'database' => $event->connection->getDatabaseName(),
2321
'bindings' => $event->bindings,
2422
'callSpot' => $this->guessCallSpot(),
2523
]);
2624
}
2725

28-
private function guessCallSpot(): array
26+
protected function sql(QueryExecuted $event): string
27+
{
28+
try {
29+
return $event->toRawSql();
30+
} catch (\Throwable) {
31+
return $this->toSQL($event);
32+
}
33+
}
34+
35+
public function toSQL(QueryExecuted $event): string
36+
{
37+
$pdo = \method_exists($event->connection, 'getPdo')
38+
? $event->connection->getPdo()
39+
: null;
40+
41+
$dateFormat = $event->connection->getQueryGrammar()->getDateFormat();
42+
43+
$bindings = $event->connection->prepareBindings($event->bindings);
44+
$bindings = \array_map(fn ($value) => $this->prepareValue($event, $pdo, $dateFormat, $value), $bindings);
45+
46+
return $this->prepareQuery($event->sql, $bindings);
47+
}
48+
49+
protected function prepareQuery(string $query, array $bindings): string
50+
{
51+
foreach ($bindings as $key => $value) {
52+
$regex = \is_numeric($key)
53+
? "/(?<!\?)\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?!\?)/"
54+
: "/:$key(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/";
55+
56+
$query = \preg_replace($regex, $value, $query, 1);
57+
}
58+
59+
return $query;
60+
}
61+
62+
protected function prepareValue(QueryExecuted $event, ?\PDO $pdo, string $dateFormat, $value): string
63+
{
64+
if (\method_exists($event->connection, 'escape')) {
65+
try {
66+
return $event->connection->escape($value);
67+
} catch (\Throwable) {
68+
}
69+
}
70+
71+
if (\is_null($value)) {
72+
return 'NULL';
73+
}
74+
75+
if (\is_bool($value)) {
76+
return $value ? '1' : '0';
77+
}
78+
79+
if (\is_int($value) || \is_float($value)) {
80+
return \strval($value);
81+
}
82+
83+
if (\is_string($value) && ! \mb_check_encoding($value, 'UTF-8')) {
84+
return $this->quote($pdo, '[BINARY DATA]');
85+
}
86+
87+
if ($value instanceof \DateTimeInterface) {
88+
$value = $value->format($dateFormat);
89+
}
90+
91+
if ($value instanceof \Stringable) {
92+
$value = \strval($value);
93+
}
94+
95+
if (\is_object($value) && \method_exists($value, 'toString')) {
96+
$value = $value->toString();
97+
}
98+
99+
// objects not implementing __toString() or toString() will fail here
100+
return $this->quote($pdo, \strval($value));
101+
}
102+
103+
protected function quote(?\PDO $pdo, string $value): string
104+
{
105+
if ($pdo) {
106+
return $pdo->quote($value);
107+
}
108+
109+
$search = ["\\", "\x00", "\n", "\r", "'", '"', "\x1a"];
110+
$replace = ["\\\\", "\\0", "\\n", "\\r", "\'", '\"', "\\Z"];
111+
112+
return "'" . \str_replace($search, $replace, $value) . "'";
113+
}
114+
115+
protected function guessCallSpot(): array
29116
{
30117
$stack = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
31118
$vendor = \DIRECTORY_SEPARATOR . 'vendor' . \DIRECTORY_SEPARATOR;

0 commit comments

Comments
 (0)