Skip to content

Commit

Permalink
mysqli
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 19, 2024
1 parent f38dd97 commit d3c465c
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 0 deletions.
170 changes: 170 additions & 0 deletions src/Database/Drivers/MySQLi/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Database\Drivers\MySQLi;

use mysqli;
use mysqli_sql_exception;
use Nette;
use Nette\Database\Drivers;


/**
* MySQLi driver.
*
* Options:
* - host => the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options}
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect}
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
*/
class Connection implements Drivers\Connection
{
private Drivers\Engines\MySQLEngine $engine;
private mysqli $db;


public function __construct(
string $host = 'localhost',
string $username = '',
string $password = '',
?string $database = null,
?int $port = 3306,
?string $socket = null,
bool $persistent = false,
int $flags = 0,
?string $charset = 'utf8mb4',
?string $sqlmode = null,
) {
if (!extension_loaded('mysqli')) {
throw new \LogicException("PHP extension 'mysqli' is not loaded.");
}

$this->engine = $this->getDatabaseEngine();
$this->db = new mysqli;
try {
$this->db->real_connect(
($persistent ? 'p:' : '') . $host,
$username,
$password,
$database,
$port,
$socket,
$flags,
);
} catch (mysqli_sql_exception $e) {
throw new ($this->convertException($e, $args, Nette\Database\ConnectionException::class))(...$args);
}

if ($charset) {
$this->db->query('SET NAMES ' . $this->db->real_escape_string($charset));
}
if ($sqlmode) {
$this->db->query('SET sql_mode=' . $this->db->real_escape_string($sqlmode));
}
}


public function getDatabaseEngine(): Drivers\Engines\MySQLEngine
{
return new Drivers\Engines\MySQLEngine($this);
}


public function convertException(
mysqli_sql_exception $e,
?array &$args,
?string $class = null,
?string $sql = null,
?array $params = null,
): string
{
$args = [$e->getMessage(), $e->getSqlState(), $e->getCode(), $sql ? new Nette\Database\SqlLiteral($sql, $params) : null, $e];
return $this->engine->determineExceptionClass($e->getCode(), $e->getSqlState(), $e->getMessage())
?? $class
?? Nette\Database\DriverException::class;
}


public function query(string $sql, array $params = []): Result
{
try {
$res = $this->db->query($sql);
} catch (mysqli_sql_exception $e) {
throw new ($this->convertException($e, $args, $sql, $params))(...$args);
}

if ($res instanceof \mysqli_result) {
return new Result($res, $this);
}

throw new \Exception; // TODO
}


public function beginTransaction(): void
{
try {
$this->db->begin_transaction();
} catch (mysqli_sql_exception $e) {
throw new ($this->convertException($e, $args))(...$args);
}
}


public function commit(): void
{
try {
$this->db->commit();
} catch (mysqli_sql_exception $e) {
throw new ($this->convertException($e, $args))(...$args);
}
}


public function rollBack(): void
{
try {
$this->db->rollback();
} catch (mysqli_sql_exception $e) {
throw new ($this->convertException($e, $args))(...$args);
}
}


public function getInsertId(?string $sequence = null): int|string
{
return $this->db->insert_id;
}


public function quote(string $string): string
{
return "'" . $this->db->real_escape_string($string) . "'";
}


public function getServerVersion(): string
{
return $this->db->get_server_info();
}


public function getNativeConnection(): mysqli
{
return $this->db;
}
}
51 changes: 51 additions & 0 deletions src/Database/Drivers/MySQLi/Result.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Database\Drivers\MySQLi;

use Nette\Database\Drivers;


class Result implements Drivers\Result
{
public function __construct(
private \mysqli_result $result,
) {
}


public function fetch(): ?array
{
return $this->result->fetch_assoc();
}


public function getColumnCount(): int
{
return $this->result->field_count;
}


public function getRowCount(): int
{
return $this->result->num_rows;
}


public function getColumnsInfo(): array
{
return [];
}


public function free(): void
{
$this->result->free_result();
}
}
1 change: 1 addition & 0 deletions src/Database/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
final class Factory
{
private const Drivers = [
'mysqli' => Drivers\MySQLi\Connection::class,
'pdo-mssql' => Drivers\PDO\MSSQL\Connection::class,
'pdo-mysql' => Drivers\PDO\MySQL\Connection::class,
'pdo-oci' => Drivers\PDO\OCI\Connection::class,
Expand Down
58 changes: 58 additions & 0 deletions tests/Database/_mysqli.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

/**
* Test: Nette\Database\Connection exceptions.
*/

declare(strict_types=1);

use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


$factory = new Nette\Database\Factory;

$connection = $factory->createFromParameters(
driver: 'mysqli',
host: 'localhost',
username: 'root',
password: 'xxx',
database: 'nette_test',
);

/*
$connection = $factory->createFromParameters([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
'database' => 'nette_test',
]);
$connection = new Nette\Database\Connection([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
'database' => 'nette_test',
]);
*/

$connection->query('SET NAMES utf8');

/*
Assert::same(1045, $e->getDriverCode());
Assert::contains($e->getSqlState(), ['HY000', '28000']);
Assert::same($e->getCode(), $e->getSqlState());
$e = Assert::exception(
fn() => $connection->rollback(),
Nette\Database\DriverException::class,
'There is no active transaction',
0,
);
Assert::same(null, $e->getDriverCode());
*/

0 comments on commit d3c465c

Please sign in to comment.