summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Driver/Mysqli
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Driver/Mysqli')
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Connection.php112
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php117
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php30
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php35
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php22
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/HostRequired.php20
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php41
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php24
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php24
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php30
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Initializer.php14
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php32
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php28
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php27
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Result.php164
-rw-r--r--vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php154
16 files changed, 874 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Connection.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Connection.php
new file mode 100644
index 0000000..cc00fb6
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Connection.php
@@ -0,0 +1,112 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli;
6
7use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
8use Doctrine\DBAL\Driver\Exception;
9use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
10use mysqli;
11use mysqli_sql_exception;
12
13final class Connection implements ConnectionInterface
14{
15 /**
16 * Name of the option to set connection flags
17 */
18 public const OPTION_FLAGS = 'flags';
19
20 /** @internal The connection can be only instantiated by its driver. */
21 public function __construct(private readonly mysqli $connection)
22 {
23 }
24
25 public function getServerVersion(): string
26 {
27 return $this->connection->get_server_info();
28 }
29
30 public function prepare(string $sql): Statement
31 {
32 try {
33 $stmt = $this->connection->prepare($sql);
34 } catch (mysqli_sql_exception $e) {
35 throw ConnectionError::upcast($e);
36 }
37
38 if ($stmt === false) {
39 throw ConnectionError::new($this->connection);
40 }
41
42 return new Statement($stmt);
43 }
44
45 public function query(string $sql): Result
46 {
47 return $this->prepare($sql)->execute();
48 }
49
50 public function quote(string $value): string
51 {
52 return "'" . $this->connection->escape_string($value) . "'";
53 }
54
55 public function exec(string $sql): int|string
56 {
57 try {
58 $result = $this->connection->query($sql);
59 } catch (mysqli_sql_exception $e) {
60 throw ConnectionError::upcast($e);
61 }
62
63 if ($result === false) {
64 throw ConnectionError::new($this->connection);
65 }
66
67 return $this->connection->affected_rows;
68 }
69
70 public function lastInsertId(): int|string
71 {
72 $lastInsertId = $this->connection->insert_id;
73
74 if ($lastInsertId === 0) {
75 throw Exception\NoIdentityValue::new();
76 }
77
78 return $this->connection->insert_id;
79 }
80
81 public function beginTransaction(): void
82 {
83 $this->connection->begin_transaction();
84 }
85
86 public function commit(): void
87 {
88 try {
89 if (! $this->connection->commit()) {
90 throw ConnectionError::new($this->connection);
91 }
92 } catch (mysqli_sql_exception $e) {
93 throw ConnectionError::upcast($e);
94 }
95 }
96
97 public function rollBack(): void
98 {
99 try {
100 if (! $this->connection->rollback()) {
101 throw ConnectionError::new($this->connection);
102 }
103 } catch (mysqli_sql_exception $e) {
104 throw ConnectionError::upcast($e);
105 }
106 }
107
108 public function getNativeConnection(): mysqli
109 {
110 return $this->connection;
111 }
112}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php
new file mode 100644
index 0000000..9855e56
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php
@@ -0,0 +1,117 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli;
6
7use Doctrine\DBAL\Driver\AbstractMySQLDriver;
8use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed;
9use Doctrine\DBAL\Driver\Mysqli\Exception\HostRequired;
10use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset;
11use Doctrine\DBAL\Driver\Mysqli\Initializer\Options;
12use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure;
13use Generator;
14use mysqli;
15use mysqli_sql_exception;
16use SensitiveParameter;
17
18final class Driver extends AbstractMySQLDriver
19{
20 /**
21 * {@inheritDoc}
22 */
23 public function connect(
24 #[SensitiveParameter]
25 array $params,
26 ): Connection {
27 if (! empty($params['persistent'])) {
28 if (! isset($params['host'])) {
29 throw HostRequired::forPersistentConnection();
30 }
31
32 $host = 'p:' . $params['host'];
33 } else {
34 $host = $params['host'] ?? '';
35 }
36
37 $connection = new mysqli();
38
39 foreach ($this->compilePreInitializers($params) as $initializer) {
40 $initializer->initialize($connection);
41 }
42
43 try {
44 $success = @$connection->real_connect(
45 $host,
46 $params['user'] ?? '',
47 $params['password'] ?? '',
48 $params['dbname'] ?? '',
49 $params['port'] ?? 0,
50 $params['unix_socket'] ?? '',
51 $params['driverOptions'][Connection::OPTION_FLAGS] ?? 0,
52 );
53 } catch (mysqli_sql_exception $e) {
54 throw ConnectionFailed::upcast($e);
55 }
56
57 if (! $success) {
58 throw ConnectionFailed::new($connection);
59 }
60
61 foreach ($this->compilePostInitializers($params) as $initializer) {
62 $initializer->initialize($connection);
63 }
64
65 return new Connection($connection);
66 }
67
68 /**
69 * @param array<string, mixed> $params
70 *
71 * @return Generator<int, Initializer>
72 */
73 private function compilePreInitializers(
74 #[SensitiveParameter]
75 array $params,
76 ): Generator {
77 unset($params['driverOptions'][Connection::OPTION_FLAGS]);
78
79 if (isset($params['driverOptions']) && $params['driverOptions'] !== []) {
80 yield new Options($params['driverOptions']);
81 }
82
83 if (
84 ! isset($params['ssl_key']) &&
85 ! isset($params['ssl_cert']) &&
86 ! isset($params['ssl_ca']) &&
87 ! isset($params['ssl_capath']) &&
88 ! isset($params['ssl_cipher'])
89 ) {
90 return;
91 }
92
93 yield new Secure(
94 $params['ssl_key'] ?? '',
95 $params['ssl_cert'] ?? '',
96 $params['ssl_ca'] ?? '',
97 $params['ssl_capath'] ?? '',
98 $params['ssl_cipher'] ?? '',
99 );
100 }
101
102 /**
103 * @param array<string, mixed> $params
104 *
105 * @return Generator<int, Initializer>
106 */
107 private function compilePostInitializers(
108 #[SensitiveParameter]
109 array $params,
110 ): Generator {
111 if (! isset($params['charset'])) {
112 return;
113 }
114
115 yield new Charset($params['charset']);
116 }
117}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php
new file mode 100644
index 0000000..d2477fd
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php
@@ -0,0 +1,30 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8use mysqli;
9use mysqli_sql_exception;
10use ReflectionProperty;
11
12/**
13 * @internal
14 *
15 * @psalm-immutable
16 */
17final class ConnectionError extends AbstractException
18{
19 public static function new(mysqli $connection): self
20 {
21 return new self($connection->error, $connection->sqlstate, $connection->errno);
22 }
23
24 public static function upcast(mysqli_sql_exception $exception): self
25 {
26 $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
27
28 return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
29 }
30}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php
new file mode 100644
index 0000000..cb3bc64
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php
@@ -0,0 +1,35 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8use mysqli;
9use mysqli_sql_exception;
10use ReflectionProperty;
11
12use function assert;
13
14/**
15 * @internal
16 *
17 * @psalm-immutable
18 */
19final class ConnectionFailed extends AbstractException
20{
21 public static function new(mysqli $connection): self
22 {
23 $error = $connection->connect_error;
24 assert($error !== null);
25
26 return new self($error, 'HY000', $connection->connect_errno);
27 }
28
29 public static function upcast(mysqli_sql_exception $exception): self
30 {
31 $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
32
33 return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
34 }
35}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php
new file mode 100644
index 0000000..6f26dbe
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php
@@ -0,0 +1,22 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8
9use function sprintf;
10
11/**
12 * @internal
13 *
14 * @psalm-immutable
15 */
16final class FailedReadingStreamOffset extends AbstractException
17{
18 public static function new(int $parameter): self
19 {
20 return new self(sprintf('Failed reading the stream resource for parameter #%d.', $parameter));
21 }
22}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/HostRequired.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/HostRequired.php
new file mode 100644
index 0000000..d3359fc
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/HostRequired.php
@@ -0,0 +1,20 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8
9/**
10 * @internal
11 *
12 * @psalm-immutable
13 */
14final class HostRequired extends AbstractException
15{
16 public static function forPersistentConnection(): self
17 {
18 return new self('The "host" parameter is required for a persistent connection');
19 }
20}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php
new file mode 100644
index 0000000..778ea64
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php
@@ -0,0 +1,41 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8use mysqli;
9use mysqli_sql_exception;
10use ReflectionProperty;
11
12use function sprintf;
13
14/**
15 * @internal
16 *
17 * @psalm-immutable
18 */
19final class InvalidCharset extends AbstractException
20{
21 public static function fromCharset(mysqli $connection, string $charset): self
22 {
23 return new self(
24 sprintf('Failed to set charset "%s": %s', $charset, $connection->error),
25 $connection->sqlstate,
26 $connection->errno,
27 );
28 }
29
30 public static function upcast(mysqli_sql_exception $exception, string $charset): self
31 {
32 $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
33
34 return new self(
35 sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()),
36 $p->getValue($exception),
37 $exception->getCode(),
38 $exception,
39 );
40 }
41}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php
new file mode 100644
index 0000000..654bb87
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php
@@ -0,0 +1,24 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8
9use function sprintf;
10
11/**
12 * @internal
13 *
14 * @psalm-immutable
15 */
16final class InvalidOption extends AbstractException
17{
18 public static function fromOption(int $option, mixed $value): self
19 {
20 return new self(
21 sprintf('Failed to set option %d with value "%s"', $option, $value),
22 );
23 }
24}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php
new file mode 100644
index 0000000..566d636
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php
@@ -0,0 +1,24 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8
9use function sprintf;
10
11/**
12 * @internal
13 *
14 * @psalm-immutable
15 */
16final class NonStreamResourceUsedAsLargeObject extends AbstractException
17{
18 public static function new(int $parameter): self
19 {
20 return new self(
21 sprintf('The resource passed as a LARGE_OBJECT parameter #%d must be of type "stream"', $parameter),
22 );
23 }
24}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php
new file mode 100644
index 0000000..991384c
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php
@@ -0,0 +1,30 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Exception;
6
7use Doctrine\DBAL\Driver\AbstractException;
8use mysqli_sql_exception;
9use mysqli_stmt;
10use ReflectionProperty;
11
12/**
13 * @internal
14 *
15 * @psalm-immutable
16 */
17final class StatementError extends AbstractException
18{
19 public static function new(mysqli_stmt $statement): self
20 {
21 return new self($statement->error, $statement->sqlstate, $statement->errno);
22 }
23
24 public static function upcast(mysqli_sql_exception $exception): self
25 {
26 $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
27
28 return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
29 }
30}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer.php
new file mode 100644
index 0000000..efab67e
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer.php
@@ -0,0 +1,14 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli;
6
7use Doctrine\DBAL\Driver\Exception;
8use mysqli;
9
10interface Initializer
11{
12 /** @throws Exception */
13 public function initialize(mysqli $connection): void;
14}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php
new file mode 100644
index 0000000..d02c768
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php
@@ -0,0 +1,32 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
6
7use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidCharset;
8use Doctrine\DBAL\Driver\Mysqli\Initializer;
9use mysqli;
10use mysqli_sql_exception;
11
12final class Charset implements Initializer
13{
14 public function __construct(private readonly string $charset)
15 {
16 }
17
18 public function initialize(mysqli $connection): void
19 {
20 try {
21 $success = $connection->set_charset($this->charset);
22 } catch (mysqli_sql_exception $e) {
23 throw InvalidCharset::upcast($e, $this->charset);
24 }
25
26 if ($success) {
27 return;
28 }
29
30 throw InvalidCharset::fromCharset($connection, $this->charset);
31 }
32}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php
new file mode 100644
index 0000000..3223951
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php
@@ -0,0 +1,28 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
6
7use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidOption;
8use Doctrine\DBAL\Driver\Mysqli\Initializer;
9use mysqli;
10
11use function mysqli_options;
12
13final class Options implements Initializer
14{
15 /** @param array<int,mixed> $options */
16 public function __construct(private readonly array $options)
17 {
18 }
19
20 public function initialize(mysqli $connection): void
21 {
22 foreach ($this->options as $option => $value) {
23 if (! mysqli_options($connection, $option, $value)) {
24 throw InvalidOption::fromOption($option, $value);
25 }
26 }
27 }
28}
diff --git a/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php
new file mode 100644
index 0000000..fa819b5
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php
@@ -0,0 +1,27 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
6
7use Doctrine\DBAL\Driver\Mysqli\Initializer;
8use mysqli;
9use SensitiveParameter;
10
11final class Secure implements Initializer
12{
13 public function __construct(
14 #[SensitiveParameter]
15 private readonly string $key,
16 private readonly string $cert,
17 private readonly string $ca,
18 private readonly string $capath,
19 private readonly string $cipher,
20 ) {
21 }
22
23 public function initialize(mysqli $connection): void
24 {
25 $connection->ssl_set($this->key, $this->cert, $this->ca, $this->capath, $this->cipher);
26 }
27}
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}
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}