Symfony/Doctrine: 如何使 Doctrine 与 Unix socket 一起工作?
Symfony/Doctrine: How to make Doctrine working with Unix socket?
我正在尝试使用 Unix 套接字连接到 PostgreSQL。我认为只有一种方法可以连接到主机上的数据库,但应用程序位于 Docker 中(我遵循不容器化未复制数据库的原则)。我正在使用带有最新 Doctrine 的 Symfony 4。
我用 PDO 对原始 PHP 进行了测试,它有效。我不知道如何强制 Doctrine 使用 unix 套接字。
<?php
$dbh = new PDO('pgsql:host=/var/run/postgresql;user=xxxx;dbname=zzzz;password=yyyy');
$dbh->setAttribute(PDO::ERRMODE_EXCEPTION, true);
var_dump($dbh->errorInfo());
$stmt = $dbh->query('SELECT 1 as dupa;');
$stmt->execute();
var_dump($stmt->fetchAll());
结果如预期:
www-data@ef0f2269b69a:~/html$ php o.php
array(3) {
[0]=>
string(5) "00000"
[1]=>
NULL
[2]=>
NULL
}
array(1) {
[0]=>
array(2) {
["dupa"]=>
int(1)
[0]=>
int(1)
}
}
问题是无论我在 Doctrine 中配置什么,我都会得到这个:
In PDOConnection.php line 27:
[PDOException (7)]
SQLSTATE[08006] [7] could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 5432?
could not connect to server: Cannot assign requested address
Is the server running on host "localhost" (::1) and accepting
TCP/IP connections on port 5432?
我试过 Doctrine 的语法,原始 PDO 语法,没有想法。
有没有可能是我的 pdo_pgsql 没有编译 unix socket 支持?
这是我在 Doctrine 中作为数据库 URL 尝试的:
pdo-pgsql://yyyy:xxxxx@/var/run/postgresql:5432/zzzzz
pgsql:host=/var/run/postgresql;user=xxxx;dbname=zzzz;password=yyyy
调试 Doctrine 的驱动程序后,我创建了一个不太优雅的解决方案,但它仍然有效,并且可以改进。
我不喜欢在驱动程序本身中访问环境变量,但这可能可以改进。
doctrine.yaml
doctrine:
dbal:
#driver: %database_driver%
driver_class: App\Infrastructure\Common\Service\PostgreSQLDoctrineDriver
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
#charset: UTF8
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
PostgreSQLDoctrineDriver.php
<?php declare(strict_types=1);
namespace App\Infrastructure\Common\Service;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOPgSql\Driver;
use \PDO;
use \PDOException;
use function defined;
/**
* Driver that connects through pdo_pgsql
*
* Forked original PostgreSQL driver, extended to have a possibility to pass raw PDO DSN to be
* able to connect via Unix socket
*/
class PostgreSQLDoctrineDriver extends Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$pdo = new PDOConnection(
$_SERVER['POSTGRES_DB_PDO_DSN'] ?? '',
$username,
$password,
$driverOptions
);
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')
&& (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES])
|| $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true
)
) {
$pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
/* defining client_encoding via SET NAMES to avoid inconsistent DSN support
* - the 'client_encoding' connection param only works with postgres >= 9.1
* - passing client_encoding via the 'options' param breaks pgbouncer support
*/
if (isset($params['charset'])) {
$pdo->exec('SET NAMES \'' . $params['charset'] . '\'');
}
return $pdo;
} catch (PDOException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'pdo_pgsql';
}
}
您可以通过 unix_socket
配置选项来配置套接字,该选项似乎很久以前就可用了:https://symfony.com/doc/current/reference/configuration/doctrine.html
你可以把套接字路径作为主机,例如:
doctrine:
dbal:
host: /var/pgsql_socket
以上的 None 对我有用。我在这个主题上打开了多个新问题,只是为了让它们作为这个问题的副本被及时关闭(这实际上不是同一个问题,因为我的问题更多是关于对等身份验证):How to make Symfony authenticate to PostgreSQL using peer authentication without a username and password and and Using a unix socket for Postgresql connection using Symfony 主要是反复试验, 找到了一个未记录但并不可怕的解决方案。
我们知道 PHP 和 Doctrine 都能够使用 Unix 套接字和对等身份验证进行连接,因为以下两者都有效。
$pdo = new PDO("pgsql:dbname=testing");
$pdo = EntityManager::create(['driver' => 'pdo_pgsql','dbname' => 'testing'], Setup::createAnnotationMetadataConfiguration([__DIR__."/../src"], true, null, null, false))->getConnection();
因此,有人可能会认为以下 config/packages/doctrine.yaml
会起作用:
doctrine:
dbal:
driver: pdo_pgsql
dbname: testing
server_version: 13
但没那么幸运:
[2021-07-25T22:15:57.675268+00:00] console.CRITICAL: Error thrown while running command "doctrine:schema:validate". Message: "An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied" {"exception":"[object] (Doctrine\DBAL\Exception\ConnectionException(code: 0): An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php:88)\n[previous exception] [object] (Doctrine\DBAL\Driver\PDO\Exception(code: 7): SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDO/Exception.php:18)\n[previous exception] [object] (PDOException(code: 7): SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:39)","command":"doctrine:schema:validate","message":"An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied"} []
然后我查看了给 \Doctrine\DBAL\Driver\__construct
的内容,发现本机 Doctrine 方法将 user
和 host
都保留为 null
,但 Symfony 设置不当他们分别为 root
和 localhost
。解决方案是稍微修改 config/packages/doctrine.yaml
并将 user
和 host
设置为空。虽然未显示,但显然您可以而且应该在 .env
.
中定义变量
doctrine:
dbal:
driver: pdo_pgsql
dbname: testing
server_version: 13
user: null
host: null
我正在尝试使用 Unix 套接字连接到 PostgreSQL。我认为只有一种方法可以连接到主机上的数据库,但应用程序位于 Docker 中(我遵循不容器化未复制数据库的原则)。我正在使用带有最新 Doctrine 的 Symfony 4。
我用 PDO 对原始 PHP 进行了测试,它有效。我不知道如何强制 Doctrine 使用 unix 套接字。
<?php
$dbh = new PDO('pgsql:host=/var/run/postgresql;user=xxxx;dbname=zzzz;password=yyyy');
$dbh->setAttribute(PDO::ERRMODE_EXCEPTION, true);
var_dump($dbh->errorInfo());
$stmt = $dbh->query('SELECT 1 as dupa;');
$stmt->execute();
var_dump($stmt->fetchAll());
结果如预期:
www-data@ef0f2269b69a:~/html$ php o.php
array(3) {
[0]=>
string(5) "00000"
[1]=>
NULL
[2]=>
NULL
}
array(1) {
[0]=>
array(2) {
["dupa"]=>
int(1)
[0]=>
int(1)
}
}
问题是无论我在 Doctrine 中配置什么,我都会得到这个:
In PDOConnection.php line 27:
[PDOException (7)]
SQLSTATE[08006] [7] could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 5432?
could not connect to server: Cannot assign requested address
Is the server running on host "localhost" (::1) and accepting
TCP/IP connections on port 5432?
我试过 Doctrine 的语法,原始 PDO 语法,没有想法。 有没有可能是我的 pdo_pgsql 没有编译 unix socket 支持?
这是我在 Doctrine 中作为数据库 URL 尝试的:
pdo-pgsql://yyyy:xxxxx@/var/run/postgresql:5432/zzzzz
pgsql:host=/var/run/postgresql;user=xxxx;dbname=zzzz;password=yyyy
调试 Doctrine 的驱动程序后,我创建了一个不太优雅的解决方案,但它仍然有效,并且可以改进。
我不喜欢在驱动程序本身中访问环境变量,但这可能可以改进。
doctrine.yaml
doctrine:
dbal:
#driver: %database_driver%
driver_class: App\Infrastructure\Common\Service\PostgreSQLDoctrineDriver
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
#charset: UTF8
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
PostgreSQLDoctrineDriver.php
<?php declare(strict_types=1);
namespace App\Infrastructure\Common\Service;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOPgSql\Driver;
use \PDO;
use \PDOException;
use function defined;
/**
* Driver that connects through pdo_pgsql
*
* Forked original PostgreSQL driver, extended to have a possibility to pass raw PDO DSN to be
* able to connect via Unix socket
*/
class PostgreSQLDoctrineDriver extends Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$pdo = new PDOConnection(
$_SERVER['POSTGRES_DB_PDO_DSN'] ?? '',
$username,
$password,
$driverOptions
);
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')
&& (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES])
|| $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true
)
) {
$pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
/* defining client_encoding via SET NAMES to avoid inconsistent DSN support
* - the 'client_encoding' connection param only works with postgres >= 9.1
* - passing client_encoding via the 'options' param breaks pgbouncer support
*/
if (isset($params['charset'])) {
$pdo->exec('SET NAMES \'' . $params['charset'] . '\'');
}
return $pdo;
} catch (PDOException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'pdo_pgsql';
}
}
您可以通过 unix_socket
配置选项来配置套接字,该选项似乎很久以前就可用了:https://symfony.com/doc/current/reference/configuration/doctrine.html
你可以把套接字路径作为主机,例如:
doctrine:
dbal:
host: /var/pgsql_socket
None 对我有用。我在这个主题上打开了多个新问题,只是为了让它们作为这个问题的副本被及时关闭(这实际上不是同一个问题,因为我的问题更多是关于对等身份验证):How to make Symfony authenticate to PostgreSQL using peer authentication without a username and password and and Using a unix socket for Postgresql connection using Symfony 主要是反复试验, 找到了一个未记录但并不可怕的解决方案。
我们知道 PHP 和 Doctrine 都能够使用 Unix 套接字和对等身份验证进行连接,因为以下两者都有效。
$pdo = new PDO("pgsql:dbname=testing");
$pdo = EntityManager::create(['driver' => 'pdo_pgsql','dbname' => 'testing'], Setup::createAnnotationMetadataConfiguration([__DIR__."/../src"], true, null, null, false))->getConnection();
因此,有人可能会认为以下 config/packages/doctrine.yaml
会起作用:
doctrine:
dbal:
driver: pdo_pgsql
dbname: testing
server_version: 13
但没那么幸运:
[2021-07-25T22:15:57.675268+00:00] console.CRITICAL: Error thrown while running command "doctrine:schema:validate". Message: "An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied" {"exception":"[object] (Doctrine\DBAL\Exception\ConnectionException(code: 0): An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php:88)\n[previous exception] [object] (Doctrine\DBAL\Driver\PDO\Exception(code: 7): SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDO/Exception.php:18)\n[previous exception] [object] (PDOException(code: 7): SQLSTATE[08006] [7] fe_sendauth: no password supplied at /var/www/software-requirements/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:39)","command":"doctrine:schema:validate","message":"An exception occurred in driver: SQLSTATE[08006] [7] fe_sendauth: no password supplied"} []
然后我查看了给 \Doctrine\DBAL\Driver\__construct
的内容,发现本机 Doctrine 方法将 user
和 host
都保留为 null
,但 Symfony 设置不当他们分别为 root
和 localhost
。解决方案是稍微修改 config/packages/doctrine.yaml
并将 user
和 host
设置为空。虽然未显示,但显然您可以而且应该在 .env
.
doctrine:
dbal:
driver: pdo_pgsql
dbname: testing
server_version: 13
user: null
host: null