vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php line 362

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL;
  3. use Closure;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Cache\ArrayStatement;
  6. use Doctrine\DBAL\Cache\CacheException;
  7. use Doctrine\DBAL\Cache\QueryCacheProfile;
  8. use Doctrine\DBAL\Cache\ResultCacheStatement;
  9. use Doctrine\DBAL\Driver\Connection as DriverConnection;
  10. use Doctrine\DBAL\Driver\PingableConnection;
  11. use Doctrine\DBAL\Driver\ResultStatement;
  12. use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
  13. use Doctrine\DBAL\Driver\Statement as DriverStatement;
  14. use Doctrine\DBAL\Exception\InvalidArgumentException;
  15. use Doctrine\DBAL\Platforms\AbstractPlatform;
  16. use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
  17. use Doctrine\DBAL\Query\QueryBuilder;
  18. use Doctrine\DBAL\Schema\AbstractSchemaManager;
  19. use Doctrine\DBAL\Types\Type;
  20. use Exception;
  21. use Throwable;
  22. use function array_key_exists;
  23. use function assert;
  24. use function func_get_args;
  25. use function implode;
  26. use function is_int;
  27. use function is_string;
  28. use function key;
  29. /**
  30.  * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like
  31.  * events, transaction isolation levels, configuration, emulated transaction nesting,
  32.  * lazy connecting and more.
  33.  */
  34. class Connection implements DriverConnection
  35. {
  36.     /**
  37.      * Constant for transaction isolation level READ UNCOMMITTED.
  38.      *
  39.      * @deprecated Use TransactionIsolationLevel::READ_UNCOMMITTED.
  40.      */
  41.     public const TRANSACTION_READ_UNCOMMITTED TransactionIsolationLevel::READ_UNCOMMITTED;
  42.     /**
  43.      * Constant for transaction isolation level READ COMMITTED.
  44.      *
  45.      * @deprecated Use TransactionIsolationLevel::READ_COMMITTED.
  46.      */
  47.     public const TRANSACTION_READ_COMMITTED TransactionIsolationLevel::READ_COMMITTED;
  48.     /**
  49.      * Constant for transaction isolation level REPEATABLE READ.
  50.      *
  51.      * @deprecated Use TransactionIsolationLevel::REPEATABLE_READ.
  52.      */
  53.     public const TRANSACTION_REPEATABLE_READ TransactionIsolationLevel::REPEATABLE_READ;
  54.     /**
  55.      * Constant for transaction isolation level SERIALIZABLE.
  56.      *
  57.      * @deprecated Use TransactionIsolationLevel::SERIALIZABLE.
  58.      */
  59.     public const TRANSACTION_SERIALIZABLE TransactionIsolationLevel::SERIALIZABLE;
  60.     /**
  61.      * Represents an array of ints to be expanded by Doctrine SQL parsing.
  62.      */
  63.     public const PARAM_INT_ARRAY ParameterType::INTEGER self::ARRAY_PARAM_OFFSET;
  64.     /**
  65.      * Represents an array of strings to be expanded by Doctrine SQL parsing.
  66.      */
  67.     public const PARAM_STR_ARRAY ParameterType::STRING self::ARRAY_PARAM_OFFSET;
  68.     /**
  69.      * Offset by which PARAM_* constants are detected as arrays of the param type.
  70.      */
  71.     public const ARRAY_PARAM_OFFSET 100;
  72.     /**
  73.      * The wrapped driver connection.
  74.      *
  75.      * @var \Doctrine\DBAL\Driver\Connection|null
  76.      */
  77.     protected $_conn;
  78.     /** @var Configuration */
  79.     protected $_config;
  80.     /** @var EventManager */
  81.     protected $_eventManager;
  82.     /** @var ExpressionBuilder */
  83.     protected $_expr;
  84.     /**
  85.      * Whether or not a connection has been established.
  86.      *
  87.      * @var bool
  88.      */
  89.     private $isConnected false;
  90.     /**
  91.      * The current auto-commit mode of this connection.
  92.      *
  93.      * @var bool
  94.      */
  95.     private $autoCommit true;
  96.     /**
  97.      * The transaction nesting level.
  98.      *
  99.      * @var int
  100.      */
  101.     private $transactionNestingLevel 0;
  102.     /**
  103.      * The currently active transaction isolation level.
  104.      *
  105.      * @var int
  106.      */
  107.     private $transactionIsolationLevel;
  108.     /**
  109.      * If nested transactions should use savepoints.
  110.      *
  111.      * @var bool
  112.      */
  113.     private $nestTransactionsWithSavepoints false;
  114.     /**
  115.      * The parameters used during creation of the Connection instance.
  116.      *
  117.      * @var mixed[]
  118.      */
  119.     private $params = [];
  120.     /**
  121.      * The DatabasePlatform object that provides information about the
  122.      * database platform used by the connection.
  123.      *
  124.      * @var AbstractPlatform
  125.      */
  126.     private $platform;
  127.     /**
  128.      * The schema manager.
  129.      *
  130.      * @var AbstractSchemaManager|null
  131.      */
  132.     protected $_schemaManager;
  133.     /**
  134.      * The used DBAL driver.
  135.      *
  136.      * @var Driver
  137.      */
  138.     protected $_driver;
  139.     /**
  140.      * Flag that indicates whether the current transaction is marked for rollback only.
  141.      *
  142.      * @var bool
  143.      */
  144.     private $isRollbackOnly false;
  145.     /** @var int */
  146.     protected $defaultFetchMode FetchMode::ASSOCIATIVE;
  147.     /**
  148.      * Initializes a new instance of the Connection class.
  149.      *
  150.      * @param mixed[]            $params       The connection parameters.
  151.      * @param Driver             $driver       The driver to use.
  152.      * @param Configuration|null $config       The configuration, optional.
  153.      * @param EventManager|null  $eventManager The event manager, optional.
  154.      *
  155.      * @throws DBALException
  156.      */
  157.     public function __construct(
  158.         array $params,
  159.         Driver $driver,
  160.         ?Configuration $config null,
  161.         ?EventManager $eventManager null
  162.     ) {
  163.         $this->_driver $driver;
  164.         $this->params  $params;
  165.         if (isset($params['pdo'])) {
  166.             $this->_conn       $params['pdo'];
  167.             $this->isConnected true;
  168.             unset($this->params['pdo']);
  169.         }
  170.         if (isset($params['platform'])) {
  171.             if (! $params['platform'] instanceof Platforms\AbstractPlatform) {
  172.                 throw DBALException::invalidPlatformType($params['platform']);
  173.             }
  174.             $this->platform $params['platform'];
  175.         }
  176.         // Create default config and event manager if none given
  177.         if (! $config) {
  178.             $config = new Configuration();
  179.         }
  180.         if (! $eventManager) {
  181.             $eventManager = new EventManager();
  182.         }
  183.         $this->_config       $config;
  184.         $this->_eventManager $eventManager;
  185.         $this->_expr = new Query\Expression\ExpressionBuilder($this);
  186.         $this->autoCommit $config->getAutoCommit();
  187.     }
  188.     /**
  189.      * Gets the parameters used during instantiation.
  190.      *
  191.      * @return mixed[]
  192.      */
  193.     public function getParams()
  194.     {
  195.         return $this->params;
  196.     }
  197.     /**
  198.      * Gets the name of the database this Connection is connected to.
  199.      *
  200.      * @return string
  201.      */
  202.     public function getDatabase()
  203.     {
  204.         return $this->_driver->getDatabase($this);
  205.     }
  206.     /**
  207.      * Gets the hostname of the currently connected database.
  208.      *
  209.      * @deprecated
  210.      *
  211.      * @return string|null
  212.      */
  213.     public function getHost()
  214.     {
  215.         return $this->params['host'] ?? null;
  216.     }
  217.     /**
  218.      * Gets the port of the currently connected database.
  219.      *
  220.      * @deprecated
  221.      *
  222.      * @return mixed
  223.      */
  224.     public function getPort()
  225.     {
  226.         return $this->params['port'] ?? null;
  227.     }
  228.     /**
  229.      * Gets the username used by this connection.
  230.      *
  231.      * @deprecated
  232.      *
  233.      * @return string|null
  234.      */
  235.     public function getUsername()
  236.     {
  237.         return $this->params['user'] ?? null;
  238.     }
  239.     /**
  240.      * Gets the password used by this connection.
  241.      *
  242.      * @deprecated
  243.      *
  244.      * @return string|null
  245.      */
  246.     public function getPassword()
  247.     {
  248.         return $this->params['password'] ?? null;
  249.     }
  250.     /**
  251.      * Gets the DBAL driver instance.
  252.      *
  253.      * @return Driver
  254.      */
  255.     public function getDriver()
  256.     {
  257.         return $this->_driver;
  258.     }
  259.     /**
  260.      * Gets the Configuration used by the Connection.
  261.      *
  262.      * @return Configuration
  263.      */
  264.     public function getConfiguration()
  265.     {
  266.         return $this->_config;
  267.     }
  268.     /**
  269.      * Gets the EventManager used by the Connection.
  270.      *
  271.      * @return EventManager
  272.      */
  273.     public function getEventManager()
  274.     {
  275.         return $this->_eventManager;
  276.     }
  277.     /**
  278.      * Gets the DatabasePlatform for the connection.
  279.      *
  280.      * @return AbstractPlatform
  281.      *
  282.      * @throws DBALException
  283.      */
  284.     public function getDatabasePlatform()
  285.     {
  286.         if ($this->platform === null) {
  287.             $this->detectDatabasePlatform();
  288.         }
  289.         return $this->platform;
  290.     }
  291.     /**
  292.      * Gets the ExpressionBuilder for the connection.
  293.      *
  294.      * @return ExpressionBuilder
  295.      */
  296.     public function getExpressionBuilder()
  297.     {
  298.         return $this->_expr;
  299.     }
  300.     /**
  301.      * Establishes the connection with the database.
  302.      *
  303.      * @return bool TRUE if the connection was successfully established, FALSE if
  304.      *              the connection is already open.
  305.      */
  306.     public function connect()
  307.     {
  308.         if ($this->isConnected) {
  309.             return false;
  310.         }
  311.         $driverOptions $this->params['driverOptions'] ?? [];
  312.         $user          $this->params['user'] ?? null;
  313.         $password      $this->params['password'] ?? null;
  314.         $this->_conn       $this->_driver->connect($this->params$user$password$driverOptions);
  315.         $this->isConnected true;
  316.         $this->transactionNestingLevel 0;
  317.         if ($this->autoCommit === false) {
  318.             $this->beginTransaction();
  319.         }
  320.         if ($this->_eventManager->hasListeners(Events::postConnect)) {
  321.             $eventArgs = new Event\ConnectionEventArgs($this);
  322.             $this->_eventManager->dispatchEvent(Events::postConnect$eventArgs);
  323.         }
  324.         return true;
  325.     }
  326.     /**
  327.      * Detects and sets the database platform.
  328.      *
  329.      * Evaluates custom platform class and version in order to set the correct platform.
  330.      *
  331.      * @throws DBALException If an invalid platform was specified for this connection.
  332.      */
  333.     private function detectDatabasePlatform() : void
  334.     {
  335.         $version $this->getDatabasePlatformVersion();
  336.         if ($version !== null) {
  337.             assert($this->_driver instanceof VersionAwarePlatformDriver);
  338.             $this->platform $this->_driver->createDatabasePlatformForVersion($version);
  339.         } else {
  340.             $this->platform $this->_driver->getDatabasePlatform();
  341.         }
  342.         $this->platform->setEventManager($this->_eventManager);
  343.     }
  344.     /**
  345.      * Returns the version of the related platform if applicable.
  346.      *
  347.      * Returns null if either the driver is not capable to create version
  348.      * specific platform instances, no explicit server version was specified
  349.      * or the underlying driver connection cannot determine the platform
  350.      * version without having to query it (performance reasons).
  351.      *
  352.      * @return string|null
  353.      *
  354.      * @throws Exception
  355.      */
  356.     private function getDatabasePlatformVersion()
  357.     {
  358.         // Driver does not support version specific platforms.
  359.         if (! $this->_driver instanceof VersionAwarePlatformDriver) {
  360.             return null;
  361.         }
  362.         // Explicit platform version requested (supersedes auto-detection).
  363.         if (isset($this->params['serverVersion'])) {
  364.             return $this->params['serverVersion'];
  365.         }
  366.         // If not connected, we need to connect now to determine the platform version.
  367.         if ($this->_conn === null) {
  368.             try {
  369.                 $this->connect();
  370.             } catch (Throwable $originalException) {
  371.                 if (empty($this->params['dbname'])) {
  372.                     throw $originalException;
  373.                 }
  374.                 // The database to connect to might not yet exist.
  375.                 // Retry detection without database name connection parameter.
  376.                 $databaseName           $this->params['dbname'];
  377.                 $this->params['dbname'] = null;
  378.                 try {
  379.                     $this->connect();
  380.                 } catch (Throwable $fallbackException) {
  381.                     // Either the platform does not support database-less connections
  382.                     // or something else went wrong.
  383.                     // Reset connection parameters and rethrow the original exception.
  384.                     $this->params['dbname'] = $databaseName;
  385.                     throw $originalException;
  386.                 }
  387.                 // Reset connection parameters.
  388.                 $this->params['dbname'] = $databaseName;
  389.                 $serverVersion          $this->getServerVersion();
  390.                 // Close "temporary" connection to allow connecting to the real database again.
  391.                 $this->close();
  392.                 return $serverVersion;
  393.             }
  394.         }
  395.         return $this->getServerVersion();
  396.     }
  397.     /**
  398.      * Returns the database server version if the underlying driver supports it.
  399.      *
  400.      * @return string|null
  401.      */
  402.     private function getServerVersion()
  403.     {
  404.         $connection $this->getWrappedConnection();
  405.         // Automatic platform version detection.
  406.         if ($connection instanceof ServerInfoAwareConnection && ! $connection->requiresQueryForServerVersion()) {
  407.             return $connection->getServerVersion();
  408.         }
  409.         // Unable to detect platform version.
  410.         return null;
  411.     }
  412.     /**
  413.      * Returns the current auto-commit mode for this connection.
  414.      *
  415.      * @see    setAutoCommit
  416.      *
  417.      * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise.
  418.      */
  419.     public function isAutoCommit()
  420.     {
  421.         return $this->autoCommit === true;
  422.     }
  423.     /**
  424.      * Sets auto-commit mode for this connection.
  425.      *
  426.      * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
  427.      * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
  428.      * the method commit or the method rollback. By default, new connections are in auto-commit mode.
  429.      *
  430.      * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is
  431.      * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op.
  432.      *
  433.      * @see   isAutoCommit
  434.      *
  435.      * @param bool $autoCommit True to enable auto-commit mode; false to disable it.
  436.      *
  437.      * @return void
  438.      */
  439.     public function setAutoCommit($autoCommit)
  440.     {
  441.         $autoCommit = (bool) $autoCommit;
  442.         // Mode not changed, no-op.
  443.         if ($autoCommit === $this->autoCommit) {
  444.             return;
  445.         }
  446.         $this->autoCommit $autoCommit;
  447.         // Commit all currently active transactions if any when switching auto-commit mode.
  448.         if ($this->isConnected !== true || $this->transactionNestingLevel === 0) {
  449.             return;
  450.         }
  451.         $this->commitAll();
  452.     }
  453.     /**
  454.      * Sets the fetch mode.
  455.      *
  456.      * @param int $fetchMode
  457.      *
  458.      * @return void
  459.      */
  460.     public function setFetchMode($fetchMode)
  461.     {
  462.         $this->defaultFetchMode $fetchMode;
  463.     }
  464.     /**
  465.      * Prepares and executes an SQL query and returns the first row of the result
  466.      * as an associative array.
  467.      *
  468.      * @param string         $statement The SQL query.
  469.      * @param mixed[]        $params    The query parameters.
  470.      * @param int[]|string[] $types     The query parameter types.
  471.      *
  472.      * @return mixed[]|false False is returned if no rows are found.
  473.      *
  474.      * @throws DBALException
  475.      */
  476.     public function fetchAssoc($statement, array $params = [], array $types = [])
  477.     {
  478.         return $this->executeQuery($statement$params$types)->fetch(FetchMode::ASSOCIATIVE);
  479.     }
  480.     /**
  481.      * Prepares and executes an SQL query and returns the first row of the result
  482.      * as a numerically indexed array.
  483.      *
  484.      * @param string         $statement The SQL query to be executed.
  485.      * @param mixed[]        $params    The prepared statement params.
  486.      * @param int[]|string[] $types     The query parameter types.
  487.      *
  488.      * @return mixed[]|false False is returned if no rows are found.
  489.      */
  490.     public function fetchArray($statement, array $params = [], array $types = [])
  491.     {
  492.         return $this->executeQuery($statement$params$types)->fetch(FetchMode::NUMERIC);
  493.     }
  494.     /**
  495.      * Prepares and executes an SQL query and returns the value of a single column
  496.      * of the first row of the result.
  497.      *
  498.      * @param string         $statement The SQL query to be executed.
  499.      * @param mixed[]        $params    The prepared statement params.
  500.      * @param int            $column    The 0-indexed column number to retrieve.
  501.      * @param int[]|string[] $types     The query parameter types.
  502.      *
  503.      * @return mixed|false False is returned if no rows are found.
  504.      *
  505.      * @throws DBALException
  506.      */
  507.     public function fetchColumn($statement, array $params = [], $column 0, array $types = [])
  508.     {
  509.         return $this->executeQuery($statement$params$types)->fetchColumn($column);
  510.     }
  511.     /**
  512.      * Whether an actual connection to the database is established.
  513.      *
  514.      * @return bool
  515.      */
  516.     public function isConnected()
  517.     {
  518.         return $this->isConnected;
  519.     }
  520.     /**
  521.      * Checks whether a transaction is currently active.
  522.      *
  523.      * @return bool TRUE if a transaction is currently active, FALSE otherwise.
  524.      */
  525.     public function isTransactionActive()
  526.     {
  527.         return $this->transactionNestingLevel 0;
  528.     }
  529.     /**
  530.      * Adds identifier condition to the query components
  531.      *
  532.      * @param mixed[]  $identifier Map of key columns to their values
  533.      * @param string[] $columns    Column names
  534.      * @param mixed[]  $values     Column values
  535.      * @param string[] $conditions Key conditions
  536.      *
  537.      * @throws DBALException
  538.      */
  539.     private function addIdentifierCondition(
  540.         array $identifier,
  541.         array &$columns,
  542.         array &$values,
  543.         array &$conditions
  544.     ) : void {
  545.         $platform $this->getDatabasePlatform();
  546.         foreach ($identifier as $columnName => $value) {
  547.             if ($value === null) {
  548.                 $conditions[] = $platform->getIsNullExpression($columnName);
  549.                 continue;
  550.             }
  551.             $columns[]    = $columnName;
  552.             $values[]     = $value;
  553.             $conditions[] = $columnName ' = ?';
  554.         }
  555.     }
  556.     /**
  557.      * Executes an SQL DELETE statement on a table.
  558.      *
  559.      * Table expression and columns are not escaped and are not safe for user-input.
  560.      *
  561.      * @param string         $tableExpression The expression of the table on which to delete.
  562.      * @param mixed[]        $identifier      The deletion criteria. An associative array containing column-value pairs.
  563.      * @param int[]|string[] $types           The types of identifiers.
  564.      *
  565.      * @return int The number of affected rows.
  566.      *
  567.      * @throws DBALException
  568.      * @throws InvalidArgumentException
  569.      */
  570.     public function delete($tableExpression, array $identifier, array $types = [])
  571.     {
  572.         if (empty($identifier)) {
  573.             throw InvalidArgumentException::fromEmptyCriteria();
  574.         }
  575.         $columns $values $conditions = [];
  576.         $this->addIdentifierCondition($identifier$columns$values$conditions);
  577.         return $this->executeUpdate(
  578.             'DELETE FROM ' $tableExpression ' WHERE ' implode(' AND '$conditions),
  579.             $values,
  580.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  581.         );
  582.     }
  583.     /**
  584.      * Closes the connection.
  585.      *
  586.      * @return void
  587.      */
  588.     public function close()
  589.     {
  590.         $this->_conn null;
  591.         $this->isConnected false;
  592.     }
  593.     /**
  594.      * Sets the transaction isolation level.
  595.      *
  596.      * @param int $level The level to set.
  597.      *
  598.      * @return int
  599.      */
  600.     public function setTransactionIsolation($level)
  601.     {
  602.         $this->transactionIsolationLevel $level;
  603.         return $this->executeUpdate($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level));
  604.     }
  605.     /**
  606.      * Gets the currently active transaction isolation level.
  607.      *
  608.      * @return int The current transaction isolation level.
  609.      */
  610.     public function getTransactionIsolation()
  611.     {
  612.         if ($this->transactionIsolationLevel === null) {
  613.             $this->transactionIsolationLevel $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel();
  614.         }
  615.         return $this->transactionIsolationLevel;
  616.     }
  617.     /**
  618.      * Executes an SQL UPDATE statement on a table.
  619.      *
  620.      * Table expression and columns are not escaped and are not safe for user-input.
  621.      *
  622.      * @param string         $tableExpression The expression of the table to update quoted or unquoted.
  623.      * @param mixed[]        $data            An associative array containing column-value pairs.
  624.      * @param mixed[]        $identifier      The update criteria. An associative array containing column-value pairs.
  625.      * @param int[]|string[] $types           Types of the merged $data and $identifier arrays in that order.
  626.      *
  627.      * @return int The number of affected rows.
  628.      *
  629.      * @throws DBALException
  630.      */
  631.     public function update($tableExpression, array $data, array $identifier, array $types = [])
  632.     {
  633.         $columns $values $conditions $set = [];
  634.         foreach ($data as $columnName => $value) {
  635.             $columns[] = $columnName;
  636.             $values[]  = $value;
  637.             $set[]     = $columnName ' = ?';
  638.         }
  639.         $this->addIdentifierCondition($identifier$columns$values$conditions);
  640.         if (is_string(key($types))) {
  641.             $types $this->extractTypeValues($columns$types);
  642.         }
  643.         $sql 'UPDATE ' $tableExpression ' SET ' implode(', '$set)
  644.                 . ' WHERE ' implode(' AND '$conditions);
  645.         return $this->executeUpdate($sql$values$types);
  646.     }
  647.     /**
  648.      * Inserts a table row with specified data.
  649.      *
  650.      * Table expression and columns are not escaped and are not safe for user-input.
  651.      *
  652.      * @param string         $tableExpression The expression of the table to insert data into, quoted or unquoted.
  653.      * @param mixed[]        $data            An associative array containing column-value pairs.
  654.      * @param int[]|string[] $types           Types of the inserted data.
  655.      *
  656.      * @return int The number of affected rows.
  657.      *
  658.      * @throws DBALException
  659.      */
  660.     public function insert($tableExpression, array $data, array $types = [])
  661.     {
  662.         if (empty($data)) {
  663.             return $this->executeUpdate('INSERT INTO ' $tableExpression ' () VALUES ()');
  664.         }
  665.         $columns = [];
  666.         $values  = [];
  667.         $set     = [];
  668.         foreach ($data as $columnName => $value) {
  669.             $columns[] = $columnName;
  670.             $values[]  = $value;
  671.             $set[]     = '?';
  672.         }
  673.         return $this->executeUpdate(
  674.             'INSERT INTO ' $tableExpression ' (' implode(', '$columns) . ')' .
  675.             ' VALUES (' implode(', '$set) . ')',
  676.             $values,
  677.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  678.         );
  679.     }
  680.     /**
  681.      * Extract ordered type list from an ordered column list and type map.
  682.      *
  683.      * @param int[]|string[] $columnList
  684.      * @param int[]|string[] $types
  685.      *
  686.      * @return int[]|string[]
  687.      */
  688.     private function extractTypeValues(array $columnList, array $types)
  689.     {
  690.         $typeValues = [];
  691.         foreach ($columnList as $columnIndex => $columnName) {
  692.             $typeValues[] = $types[$columnName] ?? ParameterType::STRING;
  693.         }
  694.         return $typeValues;
  695.     }
  696.     /**
  697.      * Quotes a string so it can be safely used as a table or column name, even if
  698.      * it is a reserved name.
  699.      *
  700.      * Delimiting style depends on the underlying database platform that is being used.
  701.      *
  702.      * NOTE: Just because you CAN use quoted identifiers does not mean
  703.      * you SHOULD use them. In general, they end up causing way more
  704.      * problems than they solve.
  705.      *
  706.      * @param string $str The name to be quoted.
  707.      *
  708.      * @return string The quoted name.
  709.      */
  710.     public function quoteIdentifier($str)
  711.     {
  712.         return $this->getDatabasePlatform()->quoteIdentifier($str);
  713.     }
  714.     /**
  715.      * {@inheritDoc}
  716.      */
  717.     public function quote($input$type ParameterType::STRING)
  718.     {
  719.         $connection $this->getWrappedConnection();
  720.         [$value$bindingType] = $this->getBindingInfo($input$type);
  721.         return $connection->quote($value$bindingType);
  722.     }
  723.     /**
  724.      * Prepares and executes an SQL query and returns the result as an associative array.
  725.      *
  726.      * @param string         $sql    The SQL query.
  727.      * @param mixed[]        $params The query parameters.
  728.      * @param int[]|string[] $types  The query parameter types.
  729.      *
  730.      * @return mixed[]
  731.      */
  732.     public function fetchAll($sql, array $params = [], $types = [])
  733.     {
  734.         return $this->executeQuery($sql$params$types)->fetchAll();
  735.     }
  736.     /**
  737.      * Prepares an SQL statement.
  738.      *
  739.      * @param string $statement The SQL statement to prepare.
  740.      *
  741.      * @return DriverStatement The prepared statement.
  742.      *
  743.      * @throws DBALException
  744.      */
  745.     public function prepare($statement)
  746.     {
  747.         try {
  748.             $stmt = new Statement($statement$this);
  749.         } catch (Throwable $ex) {
  750.             throw DBALException::driverExceptionDuringQuery($this->_driver$ex$statement);
  751.         }
  752.         $stmt->setFetchMode($this->defaultFetchMode);
  753.         return $stmt;
  754.     }
  755.     /**
  756.      * Executes an, optionally parametrized, SQL query.
  757.      *
  758.      * If the query is parametrized, a prepared statement is used.
  759.      * If an SQLLogger is configured, the execution is logged.
  760.      *
  761.      * @param string                 $query  The SQL query to execute.
  762.      * @param mixed[]                $params The parameters to bind to the query, if any.
  763.      * @param int[]|string[]         $types  The types the previous parameters are in.
  764.      * @param QueryCacheProfile|null $qcp    The query cache profile, optional.
  765.      *
  766.      * @return ResultStatement The executed statement.
  767.      *
  768.      * @throws DBALException
  769.      */
  770.     public function executeQuery($query, array $params = [], $types = [], ?QueryCacheProfile $qcp null)
  771.     {
  772.         if ($qcp !== null) {
  773.             return $this->executeCacheQuery($query$params$types$qcp);
  774.         }
  775.         $connection $this->getWrappedConnection();
  776.         $logger $this->_config->getSQLLogger();
  777.         if ($logger) {
  778.             $logger->startQuery($query$params$types);
  779.         }
  780.         try {
  781.             if ($params) {
  782.                 [$query$params$types] = SQLParserUtils::expandListParameters($query$params$types);
  783.                 $stmt $connection->prepare($query);
  784.                 if ($types) {
  785.                     $this->_bindTypedValues($stmt$params$types);
  786.                     $stmt->execute();
  787.                 } else {
  788.                     $stmt->execute($params);
  789.                 }
  790.             } else {
  791.                 $stmt $connection->query($query);
  792.             }
  793.         } catch (Throwable $ex) {
  794.             throw DBALException::driverExceptionDuringQuery($this->_driver$ex$query$this->resolveParams($params$types));
  795.         }
  796.         $stmt->setFetchMode($this->defaultFetchMode);
  797.         if ($logger) {
  798.             $logger->stopQuery();
  799.         }
  800.         return $stmt;
  801.     }
  802.     /**
  803.      * Executes a caching query.
  804.      *
  805.      * @param string            $query  The SQL query to execute.
  806.      * @param mixed[]           $params The parameters to bind to the query, if any.
  807.      * @param int[]|string[]    $types  The types the previous parameters are in.
  808.      * @param QueryCacheProfile $qcp    The query cache profile.
  809.      *
  810.      * @return ResultStatement
  811.      *
  812.      * @throws CacheException
  813.      */
  814.     public function executeCacheQuery($query$params$typesQueryCacheProfile $qcp)
  815.     {
  816.         $resultCache $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl();
  817.         if ($resultCache === null) {
  818.             throw CacheException::noResultDriverConfigured();
  819.         }
  820.         $connectionParams $this->getParams();
  821.         unset($connectionParams['platform']);
  822.         [$cacheKey$realKey] = $qcp->generateCacheKeys($query$params$types$connectionParams);
  823.         // fetch the row pointers entry
  824.         $data $resultCache->fetch($cacheKey);
  825.         if ($data !== false) {
  826.             // is the real key part of this row pointers map or is the cache only pointing to other cache keys?
  827.             if (isset($data[$realKey])) {
  828.                 $stmt = new ArrayStatement($data[$realKey]);
  829.             } elseif (array_key_exists($realKey$data)) {
  830.                 $stmt = new ArrayStatement([]);
  831.             }
  832.         }
  833.         if (! isset($stmt)) {
  834.             $stmt = new ResultCacheStatement($this->executeQuery($query$params$types), $resultCache$cacheKey$realKey$qcp->getLifetime());
  835.         }
  836.         $stmt->setFetchMode($this->defaultFetchMode);
  837.         return $stmt;
  838.     }
  839.     /**
  840.      * Executes an, optionally parametrized, SQL query and returns the result,
  841.      * applying a given projection/transformation function on each row of the result.
  842.      *
  843.      * @param string  $query    The SQL query to execute.
  844.      * @param mixed[] $params   The parameters, if any.
  845.      * @param Closure $function The transformation function that is applied on each row.
  846.      *                           The function receives a single parameter, an array, that
  847.      *                           represents a row of the result set.
  848.      *
  849.      * @return mixed[] The projected result of the query.
  850.      */
  851.     public function project($query, array $paramsClosure $function)
  852.     {
  853.         $result = [];
  854.         $stmt   $this->executeQuery($query$params);
  855.         while ($row $stmt->fetch()) {
  856.             $result[] = $function($row);
  857.         }
  858.         $stmt->closeCursor();
  859.         return $result;
  860.     }
  861.     /**
  862.      * Executes an SQL statement, returning a result set as a Statement object.
  863.      *
  864.      * @return \Doctrine\DBAL\Driver\Statement
  865.      *
  866.      * @throws DBALException
  867.      */
  868.     public function query()
  869.     {
  870.         $connection $this->getWrappedConnection();
  871.         $args func_get_args();
  872.         $logger $this->_config->getSQLLogger();
  873.         if ($logger) {
  874.             $logger->startQuery($args[0]);
  875.         }
  876.         try {
  877.             $statement $connection->query(...$args);
  878.         } catch (Throwable $ex) {
  879.             throw DBALException::driverExceptionDuringQuery($this->_driver$ex$args[0]);
  880.         }
  881.         $statement->setFetchMode($this->defaultFetchMode);
  882.         if ($logger) {
  883.             $logger->stopQuery();
  884.         }
  885.         return $statement;
  886.     }
  887.     /**
  888.      * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
  889.      * and returns the number of affected rows.
  890.      *
  891.      * This method supports PDO binding types as well as DBAL mapping types.
  892.      *
  893.      * @param string         $query  The SQL query.
  894.      * @param mixed[]        $params The query parameters.
  895.      * @param int[]|string[] $types  The parameter types.
  896.      *
  897.      * @return int The number of affected rows.
  898.      *
  899.      * @throws DBALException
  900.      */
  901.     public function executeUpdate($query, array $params = [], array $types = [])
  902.     {
  903.         $connection $this->getWrappedConnection();
  904.         $logger $this->_config->getSQLLogger();
  905.         if ($logger) {
  906.             $logger->startQuery($query$params$types);
  907.         }
  908.         try {
  909.             if ($params) {
  910.                 [$query$params$types] = SQLParserUtils::expandListParameters($query$params$types);
  911.                 $stmt $connection->prepare($query);
  912.                 if ($types) {
  913.                     $this->_bindTypedValues($stmt$params$types);
  914.                     $stmt->execute();
  915.                 } else {
  916.                     $stmt->execute($params);
  917.                 }
  918.                 $result $stmt->rowCount();
  919.             } else {
  920.                 $result $connection->exec($query);
  921.             }
  922.         } catch (Throwable $ex) {
  923.             throw DBALException::driverExceptionDuringQuery($this->_driver$ex$query$this->resolveParams($params$types));
  924.         }
  925.         if ($logger) {
  926.             $logger->stopQuery();
  927.         }
  928.         return $result;
  929.     }
  930.     /**
  931.      * Executes an SQL statement and return the number of affected rows.
  932.      *
  933.      * @param string $statement
  934.      *
  935.      * @return int The number of affected rows.
  936.      *
  937.      * @throws DBALException
  938.      */
  939.     public function exec($statement)
  940.     {
  941.         $connection $this->getWrappedConnection();
  942.         $logger $this->_config->getSQLLogger();
  943.         if ($logger) {
  944.             $logger->startQuery($statement);
  945.         }
  946.         try {
  947.             $result $connection->exec($statement);
  948.         } catch (Throwable $ex) {
  949.             throw DBALException::driverExceptionDuringQuery($this->_driver$ex$statement);
  950.         }
  951.         if ($logger) {
  952.             $logger->stopQuery();
  953.         }
  954.         return $result;
  955.     }
  956.     /**
  957.      * Returns the current transaction nesting level.
  958.      *
  959.      * @return int The nesting level. A value of 0 means there's no active transaction.
  960.      */
  961.     public function getTransactionNestingLevel()
  962.     {
  963.         return $this->transactionNestingLevel;
  964.     }
  965.     /**
  966.      * Fetches the SQLSTATE associated with the last database operation.
  967.      *
  968.      * @return string|null The last error code.
  969.      */
  970.     public function errorCode()
  971.     {
  972.         return $this->getWrappedConnection()->errorCode();
  973.     }
  974.     /**
  975.      * {@inheritDoc}
  976.      */
  977.     public function errorInfo()
  978.     {
  979.         return $this->getWrappedConnection()->errorInfo();
  980.     }
  981.     /**
  982.      * Returns the ID of the last inserted row, or the last value from a sequence object,
  983.      * depending on the underlying driver.
  984.      *
  985.      * Note: This method may not return a meaningful or consistent result across different drivers,
  986.      * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
  987.      * columns or sequences.
  988.      *
  989.      * @param string|null $seqName Name of the sequence object from which the ID should be returned.
  990.      *
  991.      * @return string A string representation of the last inserted ID.
  992.      */
  993.     public function lastInsertId($seqName null)
  994.     {
  995.         return $this->getWrappedConnection()->lastInsertId($seqName);
  996.     }
  997.     /**
  998.      * Executes a function in a transaction.
  999.      *
  1000.      * The function gets passed this Connection instance as an (optional) parameter.
  1001.      *
  1002.      * If an exception occurs during execution of the function or transaction commit,
  1003.      * the transaction is rolled back and the exception re-thrown.
  1004.      *
  1005.      * @param Closure $func The function to execute transactionally.
  1006.      *
  1007.      * @return mixed The value returned by $func
  1008.      *
  1009.      * @throws Exception
  1010.      * @throws Throwable
  1011.      */
  1012.     public function transactional(Closure $func)
  1013.     {
  1014.         $this->beginTransaction();
  1015.         try {
  1016.             $res $func($this);
  1017.             $this->commit();
  1018.             return $res;
  1019.         } catch (Exception $e) {
  1020.             $this->rollBack();
  1021.             throw $e;
  1022.         } catch (Throwable $e) {
  1023.             $this->rollBack();
  1024.             throw $e;
  1025.         }
  1026.     }
  1027.     /**
  1028.      * Sets if nested transactions should use savepoints.
  1029.      *
  1030.      * @param bool $nestTransactionsWithSavepoints
  1031.      *
  1032.      * @return void
  1033.      *
  1034.      * @throws ConnectionException
  1035.      */
  1036.     public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
  1037.     {
  1038.         if ($this->transactionNestingLevel 0) {
  1039.             throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
  1040.         }
  1041.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1042.             throw ConnectionException::savepointsNotSupported();
  1043.         }
  1044.         $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
  1045.     }
  1046.     /**
  1047.      * Gets if nested transactions should use savepoints.
  1048.      *
  1049.      * @return bool
  1050.      */
  1051.     public function getNestTransactionsWithSavepoints()
  1052.     {
  1053.         return $this->nestTransactionsWithSavepoints;
  1054.     }
  1055.     /**
  1056.      * Returns the savepoint name to use for nested transactions are false if they are not supported
  1057.      * "savepointFormat" parameter is not set
  1058.      *
  1059.      * @return mixed A string with the savepoint name or false.
  1060.      */
  1061.     protected function _getNestedTransactionSavePointName()
  1062.     {
  1063.         return 'DOCTRINE2_SAVEPOINT_' $this->transactionNestingLevel;
  1064.     }
  1065.     /**
  1066.      * {@inheritDoc}
  1067.      */
  1068.     public function beginTransaction()
  1069.     {
  1070.         $connection $this->getWrappedConnection();
  1071.         ++$this->transactionNestingLevel;
  1072.         $logger $this->_config->getSQLLogger();
  1073.         if ($this->transactionNestingLevel === 1) {
  1074.             if ($logger) {
  1075.                 $logger->startQuery('"START TRANSACTION"');
  1076.             }
  1077.             $connection->beginTransaction();
  1078.             if ($logger) {
  1079.                 $logger->stopQuery();
  1080.             }
  1081.         } elseif ($this->nestTransactionsWithSavepoints) {
  1082.             if ($logger) {
  1083.                 $logger->startQuery('"SAVEPOINT"');
  1084.             }
  1085.             $this->createSavepoint($this->_getNestedTransactionSavePointName());
  1086.             if ($logger) {
  1087.                 $logger->stopQuery();
  1088.             }
  1089.         }
  1090.         return true;
  1091.     }
  1092.     /**
  1093.      * {@inheritDoc}
  1094.      *
  1095.      * @throws ConnectionException If the commit failed due to no active transaction or
  1096.      *                                            because the transaction was marked for rollback only.
  1097.      */
  1098.     public function commit()
  1099.     {
  1100.         if ($this->transactionNestingLevel === 0) {
  1101.             throw ConnectionException::noActiveTransaction();
  1102.         }
  1103.         if ($this->isRollbackOnly) {
  1104.             throw ConnectionException::commitFailedRollbackOnly();
  1105.         }
  1106.         $result true;
  1107.         $connection $this->getWrappedConnection();
  1108.         $logger $this->_config->getSQLLogger();
  1109.         if ($this->transactionNestingLevel === 1) {
  1110.             if ($logger) {
  1111.                 $logger->startQuery('"COMMIT"');
  1112.             }
  1113.             $result $connection->commit();
  1114.             if ($logger) {
  1115.                 $logger->stopQuery();
  1116.             }
  1117.         } elseif ($this->nestTransactionsWithSavepoints) {
  1118.             if ($logger) {
  1119.                 $logger->startQuery('"RELEASE SAVEPOINT"');
  1120.             }
  1121.             $this->releaseSavepoint($this->_getNestedTransactionSavePointName());
  1122.             if ($logger) {
  1123.                 $logger->stopQuery();
  1124.             }
  1125.         }
  1126.         --$this->transactionNestingLevel;
  1127.         if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) {
  1128.             return $result;
  1129.         }
  1130.         $this->beginTransaction();
  1131.         return $result;
  1132.     }
  1133.     /**
  1134.      * Commits all current nesting transactions.
  1135.      */
  1136.     private function commitAll() : void
  1137.     {
  1138.         while ($this->transactionNestingLevel !== 0) {
  1139.             if ($this->autoCommit === false && $this->transactionNestingLevel === 1) {
  1140.                 // When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
  1141.                 // Therefore we need to do the final commit here and then leave to avoid an infinite loop.
  1142.                 $this->commit();
  1143.                 return;
  1144.             }
  1145.             $this->commit();
  1146.         }
  1147.     }
  1148.     /**
  1149.      * Cancels any database changes done during the current transaction.
  1150.      *
  1151.      * @return bool
  1152.      *
  1153.      * @throws ConnectionException If the rollback operation failed.
  1154.      */
  1155.     public function rollBack()
  1156.     {
  1157.         if ($this->transactionNestingLevel === 0) {
  1158.             throw ConnectionException::noActiveTransaction();
  1159.         }
  1160.         $connection $this->getWrappedConnection();
  1161.         $logger $this->_config->getSQLLogger();
  1162.         if ($this->transactionNestingLevel === 1) {
  1163.             if ($logger) {
  1164.                 $logger->startQuery('"ROLLBACK"');
  1165.             }
  1166.             $this->transactionNestingLevel 0;
  1167.             $connection->rollBack();
  1168.             $this->isRollbackOnly false;
  1169.             if ($logger) {
  1170.                 $logger->stopQuery();
  1171.             }
  1172.             if ($this->autoCommit === false) {
  1173.                 $this->beginTransaction();
  1174.             }
  1175.         } elseif ($this->nestTransactionsWithSavepoints) {
  1176.             if ($logger) {
  1177.                 $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
  1178.             }
  1179.             $this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
  1180.             --$this->transactionNestingLevel;
  1181.             if ($logger) {
  1182.                 $logger->stopQuery();
  1183.             }
  1184.         } else {
  1185.             $this->isRollbackOnly true;
  1186.             --$this->transactionNestingLevel;
  1187.         }
  1188.         return true;
  1189.     }
  1190.     /**
  1191.      * Creates a new savepoint.
  1192.      *
  1193.      * @param string $savepoint The name of the savepoint to create.
  1194.      *
  1195.      * @return void
  1196.      *
  1197.      * @throws ConnectionException
  1198.      */
  1199.     public function createSavepoint($savepoint)
  1200.     {
  1201.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1202.             throw ConnectionException::savepointsNotSupported();
  1203.         }
  1204.         $this->getWrappedConnection()->exec($this->platform->createSavePoint($savepoint));
  1205.     }
  1206.     /**
  1207.      * Releases the given savepoint.
  1208.      *
  1209.      * @param string $savepoint The name of the savepoint to release.
  1210.      *
  1211.      * @return void
  1212.      *
  1213.      * @throws ConnectionException
  1214.      */
  1215.     public function releaseSavepoint($savepoint)
  1216.     {
  1217.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1218.             throw ConnectionException::savepointsNotSupported();
  1219.         }
  1220.         if (! $this->platform->supportsReleaseSavepoints()) {
  1221.             return;
  1222.         }
  1223.         $this->getWrappedConnection()->exec($this->platform->releaseSavePoint($savepoint));
  1224.     }
  1225.     /**
  1226.      * Rolls back to the given savepoint.
  1227.      *
  1228.      * @param string $savepoint The name of the savepoint to rollback to.
  1229.      *
  1230.      * @return void
  1231.      *
  1232.      * @throws ConnectionException
  1233.      */
  1234.     public function rollbackSavepoint($savepoint)
  1235.     {
  1236.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1237.             throw ConnectionException::savepointsNotSupported();
  1238.         }
  1239.         $this->getWrappedConnection()->exec($this->platform->rollbackSavePoint($savepoint));
  1240.     }
  1241.     /**
  1242.      * Gets the wrapped driver connection.
  1243.      *
  1244.      * @return DriverConnection
  1245.      */
  1246.     public function getWrappedConnection()
  1247.     {
  1248.         $this->connect();
  1249.         return $this->_conn;
  1250.     }
  1251.     /**
  1252.      * Gets the SchemaManager that can be used to inspect or change the
  1253.      * database schema through the connection.
  1254.      *
  1255.      * @return AbstractSchemaManager
  1256.      */
  1257.     public function getSchemaManager()
  1258.     {
  1259.         if ($this->_schemaManager === null) {
  1260.             $this->_schemaManager $this->_driver->getSchemaManager($this);
  1261.         }
  1262.         return $this->_schemaManager;
  1263.     }
  1264.     /**
  1265.      * Marks the current transaction so that the only possible
  1266.      * outcome for the transaction to be rolled back.
  1267.      *
  1268.      * @return void
  1269.      *
  1270.      * @throws ConnectionException If no transaction is active.
  1271.      */
  1272.     public function setRollbackOnly()
  1273.     {
  1274.         if ($this->transactionNestingLevel === 0) {
  1275.             throw ConnectionException::noActiveTransaction();
  1276.         }
  1277.         $this->isRollbackOnly true;
  1278.     }
  1279.     /**
  1280.      * Checks whether the current transaction is marked for rollback only.
  1281.      *
  1282.      * @return bool
  1283.      *
  1284.      * @throws ConnectionException If no transaction is active.
  1285.      */
  1286.     public function isRollbackOnly()
  1287.     {
  1288.         if ($this->transactionNestingLevel === 0) {
  1289.             throw ConnectionException::noActiveTransaction();
  1290.         }
  1291.         return $this->isRollbackOnly;
  1292.     }
  1293.     /**
  1294.      * Converts a given value to its database representation according to the conversion
  1295.      * rules of a specific DBAL mapping type.
  1296.      *
  1297.      * @param mixed  $value The value to convert.
  1298.      * @param string $type  The name of the DBAL mapping type.
  1299.      *
  1300.      * @return mixed The converted value.
  1301.      */
  1302.     public function convertToDatabaseValue($value$type)
  1303.     {
  1304.         return Type::getType($type)->convertToDatabaseValue($value$this->getDatabasePlatform());
  1305.     }
  1306.     /**
  1307.      * Converts a given value to its PHP representation according to the conversion
  1308.      * rules of a specific DBAL mapping type.
  1309.      *
  1310.      * @param mixed  $value The value to convert.
  1311.      * @param string $type  The name of the DBAL mapping type.
  1312.      *
  1313.      * @return mixed The converted type.
  1314.      */
  1315.     public function convertToPHPValue($value$type)
  1316.     {
  1317.         return Type::getType($type)->convertToPHPValue($value$this->getDatabasePlatform());
  1318.     }
  1319.     /**
  1320.      * Binds a set of parameters, some or all of which are typed with a PDO binding type
  1321.      * or DBAL mapping type, to a given statement.
  1322.      *
  1323.      * @internal Duck-typing used on the $stmt parameter to support driver statements as well as
  1324.      *           raw PDOStatement instances.
  1325.      *
  1326.      * @param \Doctrine\DBAL\Driver\Statement $stmt   The statement to bind the values to.
  1327.      * @param mixed[]                         $params The map/list of named/positional parameters.
  1328.      * @param int[]|string[]                  $types  The parameter types (PDO binding types or DBAL mapping types).
  1329.      *
  1330.      * @return void
  1331.      */
  1332.     private function _bindTypedValues($stmt, array $params, array $types)
  1333.     {
  1334.         // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
  1335.         if (is_int(key($params))) {
  1336.             // Positional parameters
  1337.             $typeOffset array_key_exists(0$types) ? -0;
  1338.             $bindIndex  1;
  1339.             foreach ($params as $value) {
  1340.                 $typeIndex $bindIndex $typeOffset;
  1341.                 if (isset($types[$typeIndex])) {
  1342.                     $type                  $types[$typeIndex];
  1343.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1344.                     $stmt->bindValue($bindIndex$value$bindingType);
  1345.                 } else {
  1346.                     $stmt->bindValue($bindIndex$value);
  1347.                 }
  1348.                 ++$bindIndex;
  1349.             }
  1350.         } else {
  1351.             // Named parameters
  1352.             foreach ($params as $name => $value) {
  1353.                 if (isset($types[$name])) {
  1354.                     $type                  $types[$name];
  1355.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1356.                     $stmt->bindValue($name$value$bindingType);
  1357.                 } else {
  1358.                     $stmt->bindValue($name$value);
  1359.                 }
  1360.             }
  1361.         }
  1362.     }
  1363.     /**
  1364.      * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type.
  1365.      *
  1366.      * @param mixed           $value The value to bind.
  1367.      * @param int|string|null $type  The type to bind (PDO or DBAL).
  1368.      *
  1369.      * @return mixed[] [0] => the (escaped) value, [1] => the binding type.
  1370.      */
  1371.     private function getBindingInfo($value$type)
  1372.     {
  1373.         if (is_string($type)) {
  1374.             $type Type::getType($type);
  1375.         }
  1376.         if ($type instanceof Type) {
  1377.             $value       $type->convertToDatabaseValue($value$this->getDatabasePlatform());
  1378.             $bindingType $type->getBindingType();
  1379.         } else {
  1380.             $bindingType $type;
  1381.         }
  1382.         return [$value$bindingType];
  1383.     }
  1384.     /**
  1385.      * Resolves the parameters to a format which can be displayed.
  1386.      *
  1387.      * @internal This is a purely internal method. If you rely on this method, you are advised to
  1388.      *           copy/paste the code as this method may change, or be removed without prior notice.
  1389.      *
  1390.      * @param mixed[]        $params
  1391.      * @param int[]|string[] $types
  1392.      *
  1393.      * @return mixed[]
  1394.      */
  1395.     public function resolveParams(array $params, array $types)
  1396.     {
  1397.         $resolvedParams = [];
  1398.         // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
  1399.         if (is_int(key($params))) {
  1400.             // Positional parameters
  1401.             $typeOffset array_key_exists(0$types) ? -0;
  1402.             $bindIndex  1;
  1403.             foreach ($params as $value) {
  1404.                 $typeIndex $bindIndex $typeOffset;
  1405.                 if (isset($types[$typeIndex])) {
  1406.                     $type                       $types[$typeIndex];
  1407.                     [$value]                    = $this->getBindingInfo($value$type);
  1408.                     $resolvedParams[$bindIndex] = $value;
  1409.                 } else {
  1410.                     $resolvedParams[$bindIndex] = $value;
  1411.                 }
  1412.                 ++$bindIndex;
  1413.             }
  1414.         } else {
  1415.             // Named parameters
  1416.             foreach ($params as $name => $value) {
  1417.                 if (isset($types[$name])) {
  1418.                     $type                  $types[$name];
  1419.                     [$value]               = $this->getBindingInfo($value$type);
  1420.                     $resolvedParams[$name] = $value;
  1421.                 } else {
  1422.                     $resolvedParams[$name] = $value;
  1423.                 }
  1424.             }
  1425.         }
  1426.         return $resolvedParams;
  1427.     }
  1428.     /**
  1429.      * Creates a new instance of a SQL query builder.
  1430.      *
  1431.      * @return QueryBuilder
  1432.      */
  1433.     public function createQueryBuilder()
  1434.     {
  1435.         return new Query\QueryBuilder($this);
  1436.     }
  1437.     /**
  1438.      * Ping the server
  1439.      *
  1440.      * When the server is not available the method returns FALSE.
  1441.      * It is responsibility of the developer to handle this case
  1442.      * and abort the request or reconnect manually:
  1443.      *
  1444.      * @return bool
  1445.      *
  1446.      * @example
  1447.      *
  1448.      *   if ($conn->ping() === false) {
  1449.      *      $conn->close();
  1450.      *      $conn->connect();
  1451.      *   }
  1452.      *
  1453.      * It is undefined if the underlying driver attempts to reconnect
  1454.      * or disconnect when the connection is not available anymore
  1455.      * as long it returns TRUE when a reconnect succeeded and
  1456.      * FALSE when the connection was dropped.
  1457.      */
  1458.     public function ping()
  1459.     {
  1460.         $connection $this->getWrappedConnection();
  1461.         if ($connection instanceof PingableConnection) {
  1462.             return $connection->ping();
  1463.         }
  1464.         try {
  1465.             $this->query($this->getDatabasePlatform()->getDummySelectSQL());
  1466.             return true;
  1467.         } catch (DBALException $e) {
  1468.             return false;
  1469.         }
  1470.     }
  1471. }