Lines 87.50% 7 / 8
Functions and Methods 66.66% 2 / 3
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
QueryException 87.50% 7 / 8 66.66% 2 / 3 6.07 0.00% 0 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 getBindings 100.00% 1 / 1 100.00% 1 / 1 1
 toContextString 83.33% 5 / 6 0.00% 0 / 1 4.07
1<?php
2
3declare(strict_types=1);
4
5namespace BlueprintAU\Radiant\Database\Exceptions;
6
7/**
8 * Thrown when a SQL statement fails to execute.
9 *
10 * The message is log-safe by contract: it carries only a generic
11 * SQLSTATE-level description of the failure — never the SQL text, and never
12 * a bound value. Debuggability is opt-in via the public `$sql` property,
13 * {@see getBindings()}, and {@see toContextString()}. The underlying
14 * \PDOException is preserved as the previous exception.
15 */
16final class QueryException extends \RuntimeException
17{
18    /**
19     * @param  string  $sql
20     * @param  array<string|int, mixed>  $bindings
21     * @param  \Throwable|null  $previous
22     */
23    public function __construct(
24        public readonly string $sql,
25        protected readonly array $bindings,
26        ?\Throwable $previous = null,
27    ) {
28        parent::__construct('SQL error executing query: the driver rejected the statement.', 0, $previous);
29    }
30
31    /**
32     * The bound values for the failed statement.
33     *
34     * Values may contain request-derived personal data — log only after
35     * redaction or when the log sink is trusted.
36     *
37     * @return array<string|int, mixed>
38     */
39    public function getBindings(): array
40    {
41        return $this->bindings;
42    }
43
44    /**
45     * A full debug rendering — SQL text plus the raw bound values.
46     *
47     * @return string
48     */
49    public function toContextString(): string
50    {
51        $rendered = [];
52        foreach ($this->bindings as $key => $value) {
53            $rendered[] = is_scalar($value) || $value === null
54                ? var_export($value, true)
55                : '[' . get_debug_type($value) . ']';
56        }
57        return $this->sql . "\nBindings: " . implode(', ', $rendered);
58    }
59}