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

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