summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Driver/Mysqli/Result.php')
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Result.php164
1 files changed, 164 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php
new file mode 100644
index 0000000..8d3c47a
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php
@@ -0,0 +1,164 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli;
6
7use Doctrine\DBAL\Driver\Exception;
8use Doctrine\DBAL\Driver\FetchUtils;
9use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
10use Doctrine\DBAL\Driver\Result as ResultInterface;
11use mysqli_sql_exception;
12use mysqli_stmt;
13
14use function array_column;
15use function array_combine;
16use function array_fill;
17use function count;
18
19final class Result implements ResultInterface
20{
21 /**
22 * Whether the statement result has columns. The property should be used only after the result metadata
23 * has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined.
24 */
25 private readonly bool $hasColumns;
26
27 /**
28 * Mapping of statement result column indexes to their names. The property should be used only
29 * if the statement result has columns ({@see $hasColumns}). Otherwise, the property value is undetermined.
30 *
31 * @var array<int,string>
32 */
33 private readonly array $columnNames;
34
35 /** @var mixed[] */
36 private array $boundValues = [];
37
38 /**
39 * @internal The result can be only instantiated by its driver connection or statement.
40 *
41 * @throws Exception
42 */
43 public function __construct(private readonly mysqli_stmt $statement)
44 {
45 $meta = $statement->result_metadata();
46 $this->hasColumns = $meta !== false;
47 $this->columnNames = $meta !== false ? array_column($meta->fetch_fields(), 'name') : [];
48
49 if ($meta === false) {
50 return;
51 }
52
53 $meta->free();
54
55 // Store result of every execution which has it. Otherwise it will be impossible
56 // to execute a new statement in case if the previous one has non-fetched rows
57 // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
58 $this->statement->store_result();
59
60 // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
61 // it will have to allocate as much memory as it may be needed for the given column type
62 // (e.g. for a LONGBLOB column it's 4 gigabytes)
63 // @link https://bugs.php.net/bug.php?id=51386#1270673122
64 //
65 // Make sure that the values are bound after each execution. Otherwise, if free() has been
66 // previously called on the result, the values are unbound making the statement unusable.
67 //
68 // It's also important that row values are bound after _each_ call to store_result(). Otherwise,
69 // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
70 // to the length of the ones fetched during the previous execution.
71 $this->boundValues = array_fill(0, count($this->columnNames), null);
72
73 // The following is necessary as PHP cannot handle references to properties properly
74 $refs = &$this->boundValues;
75
76 if (! $this->statement->bind_result(...$refs)) {
77 throw StatementError::new($this->statement);
78 }
79 }
80
81 public function fetchNumeric(): array|false
82 {
83 try {
84 $ret = $this->statement->fetch();
85 } catch (mysqli_sql_exception $e) {
86 throw StatementError::upcast($e);
87 }
88
89 if ($ret === false) {
90 throw StatementError::new($this->statement);
91 }
92
93 if ($ret === null) {
94 return false;
95 }
96
97 $values = [];
98
99 foreach ($this->boundValues as $v) {
100 $values[] = $v;
101 }
102
103 return $values;
104 }
105
106 public function fetchAssociative(): array|false
107 {
108 $values = $this->fetchNumeric();
109
110 if ($values === false) {
111 return false;
112 }
113
114 return array_combine($this->columnNames, $values);
115 }
116
117 public function fetchOne(): mixed
118 {
119 return FetchUtils::fetchOne($this);
120 }
121
122 /**
123 * {@inheritDoc}
124 */
125 public function fetchAllNumeric(): array
126 {
127 return FetchUtils::fetchAllNumeric($this);
128 }
129
130 /**
131 * {@inheritDoc}
132 */
133 public function fetchAllAssociative(): array
134 {
135 return FetchUtils::fetchAllAssociative($this);
136 }
137
138 /**
139 * {@inheritDoc}
140 */
141 public function fetchFirstColumn(): array
142 {
143 return FetchUtils::fetchFirstColumn($this);
144 }
145
146 public function rowCount(): int|string
147 {
148 if ($this->hasColumns) {
149 return $this->statement->num_rows;
150 }
151
152 return $this->statement->affected_rows;
153 }
154
155 public function columnCount(): int
156 {
157 return $this->statement->field_count;
158 }
159
160 public function free(): void
161 {
162 $this->statement->free_result();
163 }
164}