src/Doctrine/DBAL/Connection.php line 58

Open in your IDE?
  1. <?php
  2. namespace App\Doctrine\DBAL;
  3. use App\Doctrine\DBAL\Driver\ServerGoneAwayExceptionsAwareInterface;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Cache\QueryCacheProfile;
  6. use Doctrine\DBAL\Configuration;
  7. use Doctrine\DBAL\Connection as DBALConnection;
  8. use Doctrine\DBAL\Driver;
  9. class Connection extends DBALConnection implements ConnectionInterface
  10. {
  11.   /** @var int */
  12.   protected $reconnectAttempts 0;
  13.   /** @var \ReflectionProperty|null */
  14.   private $selfReflectionNestingLevelProperty;
  15.   /**
  16.    * @param array $params
  17.    * @param Driver|ServerGoneAwayExceptionsAwareInterface $driver
  18.    * @param Configuration|null $config
  19.    * @param EventManager|null $eventManager
  20.    *
  21.    * @throws \Doctrine\DBAL\Exception
  22.    */
  23.   public function __construct(
  24.     array $params,
  25.     Driver $driver,
  26.     Configuration $config null,
  27.     EventManager $eventManager null
  28.   )
  29.   {
  30.     if (!$driver instanceof ServerGoneAwayExceptionsAwareInterface) {
  31.       throw new \InvalidArgumentException(
  32.         sprintf('%s needs a driver that implements ServerGoneAwayExceptionsAwareInterface'get_class($this))
  33.       );
  34.     }
  35.     if (isset($params['driverOptions']['x_reconnect_attempts'])) {
  36.       $this->reconnectAttempts = (int)$params['driverOptions']['x_reconnect_attempts'];
  37.     }
  38.     parent::__construct($params$driver$config$eventManager);
  39.   }
  40.   /**
  41.    * @param string $sql
  42.    * @param array $params
  43.    * @param array $types
  44.    * @param QueryCacheProfile|null $qcp
  45.    *
  46.    * @return \Doctrine\DBAL\Driver\Statement The executed statement.
  47.    *
  48.    * @throws \Doctrine\DBAL\Exception
  49.    */
  50.   public function executeQuery($sql, array $params = array(), $types = array(), QueryCacheProfile $qcp null)
  51.   {
  52.     $stmt null;
  53.     $attempt 0;
  54.     $retry true;
  55.     while ($retry) {
  56.       $retry false;
  57.       try {
  58.         $stmt parent::executeQuery($sql$params$types$qcp);
  59.       } catch (\Exception $e) {
  60.         if ($this->canTryAgain($attempt) && $this->isRetryableException($e$sql)) {
  61.           $this->close();
  62.           ++$attempt;
  63.           $retry true;
  64.         } else {
  65.           throw $e;
  66.         }
  67.       }
  68.     }
  69.     return $stmt;
  70.   }
  71.   /**
  72.    * @return \Doctrine\DBAL\Driver\Statement
  73.    * @throws \Exception
  74.    */
  75.   public function query()
  76.   {
  77.     $stmt null;
  78.     $args func_get_args();
  79.     $attempt 0;
  80.     $retry true;
  81.     while ($retry) {
  82.       $retry false;
  83.       try {
  84.         switch (count($args)) {
  85.           case 1:
  86.             $stmt parent::query($args[0]);
  87.             break;
  88.           case 2:
  89.             $stmt parent::query($args[0], $args[1]);
  90.             break;
  91.           case 3:
  92.             $stmt parent::query($args[0], $args[1], $args[2]);
  93.             break;
  94.           case 4:
  95.             $stmt parent::query($args[0], $args[1], $args[2], $args[3]);
  96.             break;
  97.           default:
  98.             $stmt parent::query();
  99.         }
  100.       } catch (\Exception $e) {
  101.         if ($this->canTryAgain($attempt) && $this->isRetryableException($e$args[0])) {
  102.           $this->close();
  103.           ++$attempt;
  104.           $retry true;
  105.         } else {
  106.           throw $e;
  107.         }
  108.       }
  109.     }
  110.     return $stmt;
  111.   }
  112.   /**
  113.    * @param string $query
  114.    * @param array $params
  115.    * @param array $types
  116.    *
  117.    * @return integer The number of affected rows.
  118.    *
  119.    * @throws \Exception
  120.    */
  121.   public function executeUpdate($query, array $params = [], array $types = [])
  122.   {
  123.     return $this->executeStatement($query$params$types);
  124.   }
  125.   /**
  126.    * Executes an SQL statement with the given parameters and returns the number of affected rows.
  127.    *
  128.    * Could be used for:
  129.    *  - DML statements: INSERT, UPDATE, DELETE, etc.
  130.    *  - DDL statements: CREATE, DROP, ALTER, etc.
  131.    *  - DCL statements: GRANT, REVOKE, etc.
  132.    *  - Session control statements: ALTER SESSION, SET, DECLARE, etc.
  133.    *  - Other statements that don't yield a row set.
  134.    *
  135.    * @param string $sql The statement SQL
  136.    * @param array<mixed> $params The query parameters
  137.    * @param array<int|string|null> $types The parameter types
  138.    *
  139.    * @return int The number of affected rows.
  140.    * @throws \Doctrine\DBAL\Exception
  141.    */
  142.   public function executeStatement($sql, array $params = [], array $types = [])
  143.   {
  144.     $stmt null;
  145.     $attempt 0;
  146.     $retry true;
  147.     while ($retry) {
  148.       $retry false;
  149.       try {
  150.         // use parent::executeUpdate() for RC
  151.         $stmt parent::executeStatement($sql$params$types);
  152.       } catch (\Exception $e) {
  153.         if ($this->canTryAgain($attempt) && $this->isRetryableException($e)) {
  154.           $this->close();
  155.           ++$attempt;
  156.           $retry true;
  157.         } else {
  158.           throw $e;
  159.         }
  160.       }
  161.     }
  162.     return $stmt;
  163.   }
  164.   /**
  165.    * @return bool|void
  166.    * @throws \Exception
  167.    */
  168.   public function beginTransaction()
  169.   {
  170.     if (!== $this->getTransactionNestingLevel()) {
  171.       return parent::beginTransaction();
  172.     }
  173.     $attempt 0;
  174.     $retry true;
  175.     while ($retry) {
  176.       $retry false;
  177.       try {
  178.         parent::beginTransaction();
  179.       } catch (\Exception $e) {
  180.         if ($this->canTryAgain($attempttrue) && $this->_driver->isGoneAwayException($e)) {
  181.           $this->close();
  182.           if ($this->getTransactionNestingLevel()) {
  183.             $this->resetTransactionNestingLevel();
  184.           }
  185.           ++$attempt;
  186.           $retry true;
  187.         } else {
  188.           throw $e;
  189.         }
  190.       }
  191.     }
  192.   }
  193.   /**
  194.    * @param $sql
  195.    *
  196.    * @return Statement
  197.    */
  198.   public function prepare($sql)
  199.   {
  200.     return $this->prepareWrapped($sql);
  201.   }
  202.   /**
  203.    * returns a reconnect-wrapper for Statements.
  204.    *
  205.    * @param $sql
  206.    *
  207.    * @return Statement
  208.    */
  209.   protected function prepareWrapped($sql)
  210.   {
  211.     return new Statement($sql$this);
  212.   }
  213.   /**
  214.    * do not use, only used by Statement-class
  215.    * needs to be public for access from the Statement-class.
  216.    *
  217.    * @internal
  218.    */
  219.   public function prepareUnwrapped($sql)
  220.   {
  221.     // returns the actual statement
  222.     return parent::prepare($sql);
  223.   }
  224.   /**
  225.    * Forces reconnection by doing a dummy query.
  226.    *
  227.    * @throws \Exception
  228.    */
  229.   public function refresh()
  230.   {
  231.     $this->query('SELECT 1')->execute();
  232.   }
  233.   /**
  234.    * @param $attempt
  235.    * @param bool $ignoreTransactionLevel
  236.    *
  237.    * @return bool
  238.    */
  239.   public function canTryAgain($attempt$ignoreTransactionLevel false)
  240.   {
  241.     $canByAttempt = ($attempt $this->reconnectAttempts);
  242.     $canByTransactionNestingLevel $ignoreTransactionLevel true : (=== $this->getTransactionNestingLevel());
  243.     return $canByAttempt && $canByTransactionNestingLevel;
  244.   }
  245.   /**
  246.    * @param \Exception $e
  247.    * @param string|null $query
  248.    *
  249.    * @return bool
  250.    */
  251.   public function isRetryableException(\Exception $e$query null)
  252.   {
  253.     if (null === $query || $this->isUpdateQuery($query)) {
  254.       return $this->_driver->isGoneAwayInUpdateException($e);
  255.     }
  256.     return $this->_driver->isGoneAwayException($e);
  257.   }
  258.   /**
  259.    * This is required because beginTransaction increment transactionNestingLevel
  260.    * before the real query is executed, and results incremented also on gone away error.
  261.    * This should be safe for a new established connection.
  262.    */
  263.   private function resetTransactionNestingLevel()
  264.   {
  265.     if (!$this->selfReflectionNestingLevelProperty instanceof \ReflectionProperty) {
  266.       $reflection = new \ReflectionClass(DBALConnection::class);
  267.       // Private property has been renamed in DBAL 2.9.0+
  268.       if ($reflection->hasProperty('transactionNestingLevel')) {
  269.         $this->selfReflectionNestingLevelProperty $reflection->getProperty('transactionNestingLevel');
  270.       } else {
  271.         $this->selfReflectionNestingLevelProperty $reflection->getProperty('_transactionNestingLevel');
  272.       }
  273.       $this->selfReflectionNestingLevelProperty->setAccessible(true);
  274.     }
  275.     $this->selfReflectionNestingLevelProperty->setValue($this0);
  276.   }
  277.   /**
  278.    * @param string $query
  279.    *
  280.    * @return bool
  281.    */
  282.   public function isUpdateQuery($query)
  283.   {
  284.     return !preg_match('/^[\s\n\r\t(]*(select|show|describe)[\s\n\r\t(]+/i'$query);
  285.   }
  286. }