index/src/Data/MariaDB/MariaDBBackend.php

137 lines
5.2 KiB
PHP

<?php
// MariaDBBackend.php
// Created: 2021-04-30
// Updated: 2024-04-10
namespace Index\Data\MariaDB;
use InvalidArgumentException;
use Index\Version;
use Index\Data\IDbBackend;
use Index\Data\IDbConnection;
use Index\Data\IDbConnectionInfo;
use Index\Net\EndPoint;
use Index\Net\UnixEndPoint;
/**
* Information about the MariaDB/MySQL database layer.
*/
class MariaDBBackend implements IDbBackend {
public function isAvailable(): bool {
return extension_loaded('mysqli');
}
/**
* @internal
*/
public static function intToVersion(int $version): Version {
$sub = $version % 100;
$version = floor($version / 100);
$minor = $version % 100;
$version = floor($version / 100);
$major = $version % 100;
return new Version($major, $minor, $sub);
}
/**
* Gets the version of the underlying client library.
*
* @return Version Version of the client library.
*/
public function getClientVersion(): Version {
return self::intToVersion(mysqli_get_client_version());
}
/**
* Creates a connection with a MariaDB or MySQL server.
*
* @param MariaDBConnectionInfo $connectionInfo Object that describes the desired connection.
* @return MariaDBConnection A connection with a MariaDB or MySQL server.
*/
public function createConnection(IDbConnectionInfo $connectionInfo): IDbConnection {
if(!($connectionInfo instanceof MariaDBConnectionInfo))
throw new InvalidArgumentException('$connectionInfo must by of type MariaDBConnectionInfo');
return new MariaDBConnection($connectionInfo);
}
/**
* Constructs a connection info instance from a dsn.
*
* A username and password can be specified in their respective parts of the URL.
*
* The host part of the URL can be any DNS name, or special value `:unix:`, documented further down.
*
* The path part of the URL is used as the database name. Any prefix or suffix slashes (`/`) are trimmed and others are converted to an underscore (`_`).
* Meaning `/slash/test/` is converted to `slash_test`.
*
* In order to use a Unix socket path, set the host part to `:unix:` and specify `socket=/path/to/socket.sock` in the query.
*
* Other supported query parameters are:
* - `charset=<name>`: Specifies the character set to use for the connection.
* - `init=<query>`: Any arbitrary SQL command to execute open connecting.
* - `enc_key=<path>`: Path to a private key file for SSL.
* - `enc_cert=<path>`: Path to a certificate file for SSL.
* - `enc_authority=<path>`: Path to a certificate authority file for SSL.
* - `enc_trusted_certs=<path>`: Path to a directory that contains PEM formatted SSL CA certificates.
* - `enc_ciphers=<list>`: A list of allowable ciphers to use for SSL encryption.
* - `enc_no_verify`: Disables verification of the server certificate, allows for MITM attacks.
* - `compress`: Enables protocol compression.
*
* Previously supported query parameters:
* - `enc_verify=<anything>`: Enabled verification of server certificate. Replaced with `enc_no_verify` as it now defaults to on.
*
* @param string|array $dsn DSN with connection information.
* @return MariaDBConnectionInfo MariaDB connection info.
*/
public function parseDsn(string|array $dsn): IDbConnectionInfo {
if(is_string($dsn)) {
$dsn = parse_url($dsn);
if($dsn === false)
throw new InvalidArgumentException('$dsn is not a valid uri.');
}
if(!isset($dsn['host']))
throw new InvalidArgumentException('Host is missing from DSN.');
if(!isset($dsn['path']))
throw new InvalidArgumentException('Path is missing from DSN.');
$host = $dsn['host'];
$needsUnix = $host === ':unix';
if(!$needsUnix && isset($dsn['port']))
$host .= ':' . $dsn['port'];
$user = $dsn['user'] ?? '';
$pass = $dsn['pass'] ?? '';
$endPoint = $needsUnix ? null : EndPoint::parse($host);
$dbName = str_replace('/', '_', trim($dsn['path'], '/')); // cute for table prefixes i think
parse_str(str_replace('+', '%2B', $dsn['query'] ?? ''), $query);
$unixPath = $query['socket'] ?? null;
$charSet = $query['charset'] ?? null;
$initCommand = $query['init'] ?? null;
$keyPath = $query['enc_key'] ?? null;
$certPath = $query['enc_cert'] ?? null;
$certAuthPath = $query['enc_authority'] ?? null;
$trustedCertsPath = $query['enc_trusted_certs'] ?? null;
$cipherAlgos = $query['enc_ciphers'] ?? null;
$verifyCert = !isset($query['enc_no_verify']);
$useCompression = isset($query['compress']);
if($needsUnix) {
if(empty($unixPath))
throw new InvalidArgumentException('Unix socket path is missing from DSN.');
$endPoint = new UnixEndPoint($unixPath);
}
return new MariaDBConnectionInfo(
$endPoint, $user, $pass, $dbName,
$charSet, $initCommand, $keyPath, $certPath,
$certAuthPath, $trustedCertsPath, $cipherAlgos,
$verifyCert, $useCompression
);
}
}