summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php')
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php154
1 files changed, 154 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php
new file mode 100644
index 0000000..8436fad
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php
@@ -0,0 +1,154 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli;
6
7use Doctrine\DBAL\Driver\Exception;
8use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
9use Doctrine\DBAL\Driver\Mysqli\Exception\NonStreamResourceUsedAsLargeObject;
10use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
11use Doctrine\DBAL\Driver\Statement as StatementInterface;
12use Doctrine\DBAL\ParameterType;
13use mysqli_sql_exception;
14use mysqli_stmt;
15
16use function array_fill;
17use function assert;
18use function count;
19use function feof;
20use function fread;
21use function get_resource_type;
22use function is_int;
23use function is_resource;
24use function str_repeat;
25
26final class Statement implements StatementInterface
27{
28 private const PARAMETER_TYPE_STRING = 's';
29 private const PARAMETER_TYPE_INTEGER = 'i';
30 private const PARAMETER_TYPE_BINARY = 'b';
31
32 /** @var mixed[] */
33 private array $boundValues;
34
35 private string $types;
36
37 /**
38 * Contains ref values for bindValue().
39 *
40 * @var mixed[]
41 */
42 private array $values = [];
43
44 /** @internal The statement can be only instantiated by its driver connection. */
45 public function __construct(private readonly mysqli_stmt $stmt)
46 {
47 $paramCount = $this->stmt->param_count;
48 $this->types = str_repeat(self::PARAMETER_TYPE_STRING, $paramCount);
49 $this->boundValues = array_fill(1, $paramCount, null);
50 }
51
52 public function bindValue(int|string $param, mixed $value, ParameterType $type): void
53 {
54 assert(is_int($param));
55
56 $this->types[$param - 1] = $this->convertParameterType($type);
57 $this->values[$param] = $value;
58 $this->boundValues[$param] =& $this->values[$param];
59 }
60
61 public function execute(): Result
62 {
63 if (count($this->boundValues) > 0) {
64 $this->bindParameters();
65 }
66
67 try {
68 if (! $this->stmt->execute()) {
69 throw StatementError::new($this->stmt);
70 }
71 } catch (mysqli_sql_exception $e) {
72 throw StatementError::upcast($e);
73 }
74
75 return new Result($this->stmt);
76 }
77
78 /**
79 * Binds parameters with known types previously bound to the statement
80 *
81 * @throws Exception
82 */
83 private function bindParameters(): void
84 {
85 $streams = $values = [];
86 $types = $this->types;
87
88 foreach ($this->boundValues as $parameter => $value) {
89 assert(is_int($parameter));
90 if (! isset($types[$parameter - 1])) {
91 $types[$parameter - 1] = self::PARAMETER_TYPE_STRING;
92 }
93
94 if ($types[$parameter - 1] === self::PARAMETER_TYPE_BINARY) {
95 if (is_resource($value)) {
96 if (get_resource_type($value) !== 'stream') {
97 throw NonStreamResourceUsedAsLargeObject::new($parameter);
98 }
99
100 $streams[$parameter] = $value;
101 $values[$parameter] = null;
102 continue;
103 }
104
105 $types[$parameter - 1] = self::PARAMETER_TYPE_STRING;
106 }
107
108 $values[$parameter] = $value;
109 }
110
111 if (! $this->stmt->bind_param($types, ...$values)) {
112 throw StatementError::new($this->stmt);
113 }
114
115 $this->sendLongData($streams);
116 }
117
118 /**
119 * Handle $this->_longData after regular query parameters have been bound
120 *
121 * @param array<int, resource> $streams
122 *
123 * @throws Exception
124 */
125 private function sendLongData(array $streams): void
126 {
127 foreach ($streams as $paramNr => $stream) {
128 while (! feof($stream)) {
129 $chunk = fread($stream, 8192);
130
131 if ($chunk === false) {
132 throw FailedReadingStreamOffset::new($paramNr);
133 }
134
135 if (! $this->stmt->send_long_data($paramNr - 1, $chunk)) {
136 throw StatementError::new($this->stmt);
137 }
138 }
139 }
140 }
141
142 private function convertParameterType(ParameterType $type): string
143 {
144 return match ($type) {
145 ParameterType::NULL,
146 ParameterType::STRING,
147 ParameterType::ASCII,
148 ParameterType::BINARY => self::PARAMETER_TYPE_STRING,
149 ParameterType::INTEGER,
150 ParameterType::BOOLEAN => self::PARAMETER_TYPE_INTEGER,
151 ParameterType::LARGE_OBJECT => self::PARAMETER_TYPE_BINARY,
152 };
153 }
154}