【问题标题】:Symfony/Doctrine: How to make Doctrine working with Unix socket?Symfony/Doctrine:如何使 Doctrine 与 Unix 套接字一起工作?
【发布时间】:2021-10-01 21:13:23
【问题描述】:

我正在尝试使用 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 套接字支持?

这是我在 Doctrine 中尝试的数据库 URL:

pdo-pgsql://yyyy:xxxxx@/var/run/postgresql:5432/zzzzz
pgsql:host=/var/run/postgresql;user=xxxx;dbname=zzzz;password=yyyy

【问题讨论】:

    标签: php postgresql symfony pdo doctrine-orm


    【解决方案1】:

    以上都不适合我。我打开了关于这个主题的多个新问题,只是为了让它们作为这个问题的副本被迅速关闭(这真的不是同一个问题,因为我的问题更多的是关于对等身份验证):How to make Symfony authenticate to PostgreSQL using peer authentication without a username and passwordUsing 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 方法将 userhost 都保留为 null 但 Symfony 不恰当地将它们分别设置为 rootlocalhost .解决方案是稍微修改config/packages/doctrine.yaml 并将userhost 设置为null。虽然未显示,但显然您可以并且应该在 .env 中定义变量。

    doctrine:
        dbal:
            driver: pdo_pgsql
            dbname: testing
            server_version: 13
            user: null
            host: null
    

    【讨论】:

      【解决方案2】:

      调试 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';
          }
      }
      

      【讨论】:

        【解决方案3】:

        可以把socket路径作为主机,例如:

        doctrine:
            dbal:
                host: /var/pgsql_socket
        

        【讨论】:

        • 我没有尝试 YAML 语法,但是 DATABASE_URL 语法不适用于 host = socket 路径选项(该学说是验证主机并告诉它不是主机)跨度>
        • 是的,不幸的是,对于 postgresql 和套接字连接,我们无法使用 DATABASE_URL。我们必须在配置中单独提供每个参数。
        【解决方案4】:

        您可以通过unix_socket 配置选项配置套接字,这似乎是很久以前可用的:https://symfony.com/doc/current/reference/configuration/doctrine.html

        【讨论】:

        • 我试过了,pdo_pgsql不可用。你可以在doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/…那里查看只有mysql支持。
        • 嗯,将主机更改为套接字路径是否有效? github.com/doctrine/dbal/issues/3624
        • 没有。 Doctrine 验证主机并说它是无效的。验证在其他层上,而不是在驱动程序本身上。
        • 我正在尝试像 pdo-pgsql://user:password@/var/run/postgresql:5432/dbname 这样的 url 它不起作用。我会再次检查我的学说版本,如果#3624 问题中的那个人说它对他有用,也许我可以升级一些东西。如果在某些新的学说版本中修复/添加了套接字支持,那么文档应该说明这一点......
        • 只有mysql驱动支持unix_socket配置选项
        猜你喜欢
        • 1970-01-01
        • 2022-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多