From 89a99679a53fcf0ec4b7bbe0b07c243ddb84575c Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 9 Nov 2024 15:53:17 +0100 Subject: [PATCH] Move adjustment of options to constructor Move adjustment of connection-specific options like e.g. host, port and socket from connect method to constructor and refactor the extractHostPortSocket for that purpose and add handling of ipv6 squared brackets --- src/DatabaseDriver.php | 54 +++++++++++++---------- src/Mysqli/MysqliDriver.php | 7 +-- src/Pdo/PdoDriver.php | 87 ++++++++++++++++++++++++++----------- 3 files changed, 96 insertions(+), 52 deletions(-) diff --git a/src/DatabaseDriver.php b/src/DatabaseDriver.php index 87e77e38..b1ef131f 100644 --- a/src/DatabaseDriver.php +++ b/src/DatabaseDriver.php @@ -1897,59 +1897,67 @@ public function updateObject($table, &$object, $key, $nulls = false) /** * Extract pure host name (or IP address) and port or socket from host name option. * - * @param integer $defaultPort The default port number to be used if no port is given. + * @param string $host Host given in options used to configure the connection. + * @param integer|null $port Port given in options used to configure the connection, null if none. + * @param string|null $socket Socket given in options used to configure the connection, null if none. + * @param integer $defaultPort The default port number to be used if no port is given. + * @param boolean $ipv6SquareBrackets True if database connector uses ipv6 address with square brackets, false if not. * - * @since __DEPLOY_VERSION__ + * @return array Array with host, port and socket. + * + * @since __DEPLOY_VERSION__ */ - protected function setHostPortSocket($defaultPort) + protected function extractHostPortSocket(string $host, ?int $port, ?string $socket, int $defaultPort, bool $ipv6SquareBrackets = true): array { - $port = $this->options['port'] ?? $defaultPort; + $portNew = $port ?? $defaultPort; - if (preg_match('/^unix:(?P[^:]+)$/', $this->options['host'], $matches)) { + if (preg_match('/^unix:(?P[^:]+)$/', $host, $matches)) { // UNIX socket URI, e.g. 'unix:/path/to/unix/socket.sock' - $this->options['host'] = null; - $this->options['socket'] = $matches['socket']; - $this->options['port'] = null; + $host = null; + $socket = $matches['socket']; + $port = null; } elseif ( preg_match( '/^(?P((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P.+))?$/', - $this->options['host'], + $host, $matches ) ) { // It's an IPv4 address with or without port - $this->options['host'] = $matches['host']; + $host = $matches['host']; if (!empty($matches['port'])) { - $port = $matches['port']; + $portNew = $matches['port']; } - } elseif (preg_match('/^(?P\[.*\])(:(?P.+))?$/', $this->options['host'], $matches)) { + } elseif (preg_match('/^(?P\[.*\])(:(?P.+))?$/', $host, $matches)) { // We assume square-bracketed IPv6 address with or without port, e.g. [fe80:102::2%eth1]:3306 - $this->options['host'] = $matches['host']; + $host = $ipv6SquareBrackets ? $matches['host'] : rtrim(ltrim($matches['host'], '['), ']'); if (!empty($matches['port'])) { - $port = $matches['port']; + $portNew = $matches['port']; } - } elseif (preg_match('/^(?P(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P[^:]+))?$/i', $this->options['host'], $matches)) { + } elseif (preg_match('/^(?P(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P[^:]+))?$/i', $host, $matches)) { // Named host (e.g example.com or localhost) with or without port - $this->options['host'] = $matches['host']; + $host = $matches['host']; if (!empty($matches['port'])) { - $port = $matches['port']; + $portNew = $matches['port']; } - } elseif (preg_match('/^:(?P[^:]+)$/', $this->options['host'], $matches)) { + } elseif (preg_match('/^:(?P[^:]+)$/', $host, $matches)) { // Empty host, just port, e.g. ':3306' - $this->options['host'] = 'localhost'; - $port = $matches['port']; + $host = 'localhost'; + $portNew = $matches['port']; } // ... else we assume normal (naked) IPv6 address, so host and port stay as they are or default // Get the port number or socket name - if (is_numeric($port)) { - $this->options['port'] = (int) $port; + if (is_numeric($portNew)) { + $port = (int) $portNew; } else { - $this->options['socket'] = $port; + $socket = $portNew; } + + return [$host, $port, $socket]; } } diff --git a/src/Mysqli/MysqliDriver.php b/src/Mysqli/MysqliDriver.php index dbc6977c..0e0f791c 100644 --- a/src/Mysqli/MysqliDriver.php +++ b/src/Mysqli/MysqliDriver.php @@ -136,6 +136,10 @@ public function __construct(array $options) $options['ssl']['verify_server_cert'] = isset($options['ssl']['verify_server_cert']) ? $options['ssl']['verify_server_cert'] : null; } + // Extract host and port or socket from host option + [$options['host'], $options['port'], $options['socket']] + = $this->extractHostPortSocket($options['host'], $options['port'], $options['socket'], 3306); + // Finalize initialisation. parent::__construct($options); } @@ -198,9 +202,6 @@ public function connect() throw new UnsupportedAdapterException('The MySQLi extension is not available'); } - // Extract host and port or socket from host option - $this->setHostPortSocket(3306); - $this->connection = mysqli_init(); $connectionFlags = 0; diff --git a/src/Pdo/PdoDriver.php b/src/Pdo/PdoDriver.php index 6bdfe857..cfb3d109 100644 --- a/src/Pdo/PdoDriver.php +++ b/src/Pdo/PdoDriver.php @@ -80,7 +80,7 @@ public function __construct(array $options) $options['password'] = $options['password'] ?? ''; $options['driverOptions'] = $options['driverOptions'] ?? []; $options['ssl'] = isset($options['ssl']) ? $options['ssl'] : []; - $options['socket'] = \strpos($options['host'], 'unix:') !== false ? \str_replace('unix:', '', $options['host']) : null; + $options['socket'] = $options['socket'] ?? null; if ($options['ssl'] !== []) { $options['ssl']['enable'] = isset($options['ssl']['enable']) ? $options['ssl']['enable'] : false; @@ -92,6 +92,65 @@ public function __construct(array $options) $options['ssl']['verify_server_cert'] = isset($options['ssl']['verify_server_cert']) ? $options['ssl']['verify_server_cert'] : null; } + // Sanitize options + switch ($options['driver']) { + case 'cubrid': + $options['port'] = $options['port'] ?? 33000; + + break; + + case 'dblib': + case 'mssql': + case 'sybase': + $options['port'] = $options['port'] ?? 1433; + + break; + + case 'firebird': + $options['port'] = $options['port'] ?? 3050; + + break; + + case 'ibm': + $options['port'] = $options['port'] ?? 56789; + + break; + + case 'informix': + $options['port'] = $options['port'] ?? 1526; + $options['protocol'] = $options['protocol'] ?? 'onsoctcp'; + + break; + + case 'mysql': + // Extract host and port or socket from host option + [$options['host'], $options['port'], $options['socket']] + = $this->extractHostPortSocket($options['host'], $options['port'], $options['socket'], 3306); + + break; + + case 'oci': + $options['port'] = $options['port'] ?? 1521; + $options['charset'] = $options['charset'] ?? 'AL32UTF8'; + + break; + + case 'pgsql': + // Extract host and port or socket from host option and remove square brackets around ipv6 address + [$options['host'], $options['port'], $options['socket']] + = $this->extractHostPortSocket($options['host'], $options['port'], $options['socket'], 5432, false); + + break; + + case 'sqlite': + $options['version'] = isset($options['version']) ? (int) $options['version'] : 1; + + break; + + default: + // Do nothing + } + // Finalize initialisation parent::__construct($options); } @@ -128,8 +187,6 @@ public function connect() // Find the correct PDO DSN Format to use: switch ($this->options['driver']) { case 'cubrid': - $this->options['port'] = $this->options['port'] ?? 33000; - $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = ['#HOST#', '#PORT#', '#DBNAME#']; @@ -138,8 +195,6 @@ public function connect() break; case 'dblib': - $this->options['port'] = $this->options['port'] ?? 1433; - $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = ['#HOST#', '#PORT#', '#DBNAME#']; @@ -148,8 +203,6 @@ public function connect() break; case 'firebird': - $this->options['port'] = $this->options['port'] ?? 3050; - $format = 'firebird:dbname=#DBNAME#'; $replace = ['#DBNAME#']; @@ -158,8 +211,6 @@ public function connect() break; case 'ibm': - $this->options['port'] = $this->options['port'] ?? 56789; - if (!empty($this->options['dsn'])) { $format = 'ibm:DSN=#DSN#'; @@ -175,9 +226,6 @@ public function connect() break; case 'informix': - $this->options['port'] = $this->options['port'] ?? 1526; - $this->options['protocol'] = $this->options['protocol'] ?? 'onsoctcp'; - if (!empty($this->options['dsn'])) { $format = 'informix:DSN=#DSN#'; @@ -199,8 +247,6 @@ public function connect() break; case 'mssql': - $this->options['port'] = $this->options['port'] ?? 1433; - $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = ['#HOST#', '#PORT#', '#DBNAME#']; @@ -209,9 +255,6 @@ public function connect() break; case 'mysql': - // Extract host and port or socket from host option - $this->setHostPortSocket(3306); - if ($this->options['socket'] !== null) { $format = 'mysql:unix_socket=#SOCKET#;dbname=#DBNAME#;charset=#CHARSET#'; } else { @@ -230,9 +273,6 @@ public function connect() break; case 'oci': - $this->options['port'] = $this->options['port'] ?? 1521; - $this->options['charset'] = $this->options['charset'] ?? 'AL32UTF8'; - if (!empty($this->options['dsn'])) { $format = 'oci:dbname=#DSN#'; @@ -258,9 +298,6 @@ public function connect() break; case 'pgsql': - // Extract host and port or socket from host option - $this->setHostPortSocket(5432); - if ($this->options['socket'] !== null) { $format = 'pgsql:host=#SOCKET#;dbname=#DBNAME#'; } else { @@ -297,7 +334,7 @@ public function connect() break; case 'sqlite': - if (isset($this->options['version']) && $this->options['version'] == 2) { + if ($this->options['version'] === 2) { $format = 'sqlite2:#DBNAME#'; } else { $format = 'sqlite:#DBNAME#'; @@ -309,8 +346,6 @@ public function connect() break; case 'sybase': - $this->options['port'] = $this->options['port'] ?? 1433; - $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = ['#HOST#', '#PORT#', '#DBNAME#'];