diff options
Diffstat (limited to 'vendor/doctrine/dbal/src/Driver/Mysqli')
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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Connection as ConnectionInterface; | ||
8 | use Doctrine\DBAL\Driver\Exception; | ||
9 | use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; | ||
10 | use mysqli; | ||
11 | use mysqli_sql_exception; | ||
12 | |||
13 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractMySQLDriver; | ||
8 | use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed; | ||
9 | use Doctrine\DBAL\Driver\Mysqli\Exception\HostRequired; | ||
10 | use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset; | ||
11 | use Doctrine\DBAL\Driver\Mysqli\Initializer\Options; | ||
12 | use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure; | ||
13 | use Generator; | ||
14 | use mysqli; | ||
15 | use mysqli_sql_exception; | ||
16 | use SensitiveParameter; | ||
17 | |||
18 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | use mysqli; | ||
9 | use mysqli_sql_exception; | ||
10 | use ReflectionProperty; | ||
11 | |||
12 | /** | ||
13 | * @internal | ||
14 | * | ||
15 | * @psalm-immutable | ||
16 | */ | ||
17 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | use mysqli; | ||
9 | use mysqli_sql_exception; | ||
10 | use ReflectionProperty; | ||
11 | |||
12 | use function assert; | ||
13 | |||
14 | /** | ||
15 | * @internal | ||
16 | * | ||
17 | * @psalm-immutable | ||
18 | */ | ||
19 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | |||
9 | use function sprintf; | ||
10 | |||
11 | /** | ||
12 | * @internal | ||
13 | * | ||
14 | * @psalm-immutable | ||
15 | */ | ||
16 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | |||
9 | /** | ||
10 | * @internal | ||
11 | * | ||
12 | * @psalm-immutable | ||
13 | */ | ||
14 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | use mysqli; | ||
9 | use mysqli_sql_exception; | ||
10 | use ReflectionProperty; | ||
11 | |||
12 | use function sprintf; | ||
13 | |||
14 | /** | ||
15 | * @internal | ||
16 | * | ||
17 | * @psalm-immutable | ||
18 | */ | ||
19 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | |||
9 | use function sprintf; | ||
10 | |||
11 | /** | ||
12 | * @internal | ||
13 | * | ||
14 | * @psalm-immutable | ||
15 | */ | ||
16 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | |||
9 | use function sprintf; | ||
10 | |||
11 | /** | ||
12 | * @internal | ||
13 | * | ||
14 | * @psalm-immutable | ||
15 | */ | ||
16 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Exception; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\AbstractException; | ||
8 | use mysqli_sql_exception; | ||
9 | use mysqli_stmt; | ||
10 | use ReflectionProperty; | ||
11 | |||
12 | /** | ||
13 | * @internal | ||
14 | * | ||
15 | * @psalm-immutable | ||
16 | */ | ||
17 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Exception; | ||
8 | use mysqli; | ||
9 | |||
10 | interface 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidCharset; | ||
8 | use Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
9 | use mysqli; | ||
10 | use mysqli_sql_exception; | ||
11 | |||
12 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidOption; | ||
8 | use Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
9 | use mysqli; | ||
10 | |||
11 | use function mysqli_options; | ||
12 | |||
13 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Mysqli\Initializer; | ||
8 | use mysqli; | ||
9 | use SensitiveParameter; | ||
10 | |||
11 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Exception; | ||
8 | use Doctrine\DBAL\Driver\FetchUtils; | ||
9 | use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; | ||
10 | use Doctrine\DBAL\Driver\Result as ResultInterface; | ||
11 | use mysqli_sql_exception; | ||
12 | use mysqli_stmt; | ||
13 | |||
14 | use function array_column; | ||
15 | use function array_combine; | ||
16 | use function array_fill; | ||
17 | use function count; | ||
18 | |||
19 | final 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Driver\Mysqli; | ||
6 | |||
7 | use Doctrine\DBAL\Driver\Exception; | ||
8 | use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset; | ||
9 | use Doctrine\DBAL\Driver\Mysqli\Exception\NonStreamResourceUsedAsLargeObject; | ||
10 | use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; | ||
11 | use Doctrine\DBAL\Driver\Statement as StatementInterface; | ||
12 | use Doctrine\DBAL\ParameterType; | ||
13 | use mysqli_sql_exception; | ||
14 | use mysqli_stmt; | ||
15 | |||
16 | use function array_fill; | ||
17 | use function assert; | ||
18 | use function count; | ||
19 | use function feof; | ||
20 | use function fread; | ||
21 | use function get_resource_type; | ||
22 | use function is_int; | ||
23 | use function is_resource; | ||
24 | use function str_repeat; | ||
25 | |||
26 | final 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 | } | ||