vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php line 173

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL;
  3. use Doctrine\DBAL\Abstraction\Result;
  4. use Doctrine\DBAL\Driver\Exception;
  5. use Doctrine\DBAL\Driver\Statement as DriverStatement;
  6. use Doctrine\DBAL\Exception\NoKeyValue;
  7. use Doctrine\DBAL\Platforms\AbstractPlatform;
  8. use Doctrine\DBAL\Types\Type;
  9. use IteratorAggregate;
  10. use PDO;
  11. use PDOStatement;
  12. use Throwable;
  13. use Traversable;
  14. use function array_shift;
  15. use function is_array;
  16. use function is_string;
  17. /**
  18.  * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support
  19.  * for logging, DBAL mapping types, etc.
  20.  */
  21. class Statement implements IteratorAggregateDriverStatementResult
  22. {
  23.     /**
  24.      * The SQL statement.
  25.      *
  26.      * @var string
  27.      */
  28.     protected $sql;
  29.     /**
  30.      * The bound parameters.
  31.      *
  32.      * @var mixed[]
  33.      */
  34.     protected $params = [];
  35.     /**
  36.      * The parameter types.
  37.      *
  38.      * @var int[]|string[]
  39.      */
  40.     protected $types = [];
  41.     /**
  42.      * The underlying driver statement.
  43.      *
  44.      * @var \Doctrine\DBAL\Driver\Statement
  45.      */
  46.     protected $stmt;
  47.     /**
  48.      * The underlying database platform.
  49.      *
  50.      * @var AbstractPlatform
  51.      */
  52.     protected $platform;
  53.     /**
  54.      * The connection this statement is bound to and executed on.
  55.      *
  56.      * @var Connection
  57.      */
  58.     protected $conn;
  59.     /**
  60.      * Creates a new <tt>Statement</tt> for the given SQL and <tt>Connection</tt>.
  61.      *
  62.      * @internal The statement can be only instantiated by {@link Connection}.
  63.      *
  64.      * @param string     $sql  The SQL of the statement.
  65.      * @param Connection $conn The connection on which the statement should be executed.
  66.      */
  67.     public function __construct($sqlConnection $conn)
  68.     {
  69.         $this->sql      $sql;
  70.         $this->stmt     $conn->getWrappedConnection()->prepare($sql);
  71.         $this->conn     $conn;
  72.         $this->platform $conn->getDatabasePlatform();
  73.     }
  74.     /**
  75.      * Binds a parameter value to the statement.
  76.      *
  77.      * The value can optionally be bound with a PDO binding type or a DBAL mapping type.
  78.      * If bound with a DBAL mapping type, the binding type is derived from the mapping
  79.      * type and the value undergoes the conversion routines of the mapping type before
  80.      * being bound.
  81.      *
  82.      * @param string|int $param The name or position of the parameter.
  83.      * @param mixed      $value The value of the parameter.
  84.      * @param mixed      $type  Either a PDO binding type or a DBAL mapping type name or instance.
  85.      *
  86.      * @return bool TRUE on success, FALSE on failure.
  87.      */
  88.     public function bindValue($param$value$type ParameterType::STRING)
  89.     {
  90.         $this->params[$param] = $value;
  91.         $this->types[$param]  = $type;
  92.         if ($type !== null) {
  93.             if (is_string($type)) {
  94.                 $type Type::getType($type);
  95.             }
  96.             if ($type instanceof Type) {
  97.                 $value       $type->convertToDatabaseValue($value$this->platform);
  98.                 $bindingType $type->getBindingType();
  99.             } else {
  100.                 $bindingType $type;
  101.             }
  102.             return $this->stmt->bindValue($param$value$bindingType);
  103.         }
  104.         return $this->stmt->bindValue($param$value);
  105.     }
  106.     /**
  107.      * Binds a parameter to a value by reference.
  108.      *
  109.      * Binding a parameter by reference does not support DBAL mapping types.
  110.      *
  111.      * @param string|int $param    The name or position of the parameter.
  112.      * @param mixed      $variable The reference to the variable to bind.
  113.      * @param int        $type     The PDO binding type.
  114.      * @param int|null   $length   Must be specified when using an OUT bind
  115.      *                             so that PHP allocates enough memory to hold the returned value.
  116.      *
  117.      * @return bool TRUE on success, FALSE on failure.
  118.      */
  119.     public function bindParam($param, &$variable$type ParameterType::STRING$length null)
  120.     {
  121.         $this->params[$param] = $variable;
  122.         $this->types[$param]  = $type;
  123.         if ($this->stmt instanceof PDOStatement) {
  124.             $length $length ?? 0;
  125.         }
  126.         return $this->stmt->bindParam($param$variable$type$length);
  127.     }
  128.     /**
  129.      * Executes the statement with the currently bound parameters.
  130.      *
  131.      * @param mixed[]|null $params
  132.      *
  133.      * @return bool TRUE on success, FALSE on failure.
  134.      *
  135.      * @throws Exception
  136.      */
  137.     public function execute($params null)
  138.     {
  139.         if (is_array($params)) {
  140.             $this->params $params;
  141.         }
  142.         $logger $this->conn->getConfiguration()->getSQLLogger();
  143.         if ($logger) {
  144.             $logger->startQuery($this->sql$this->params$this->types);
  145.         }
  146.         try {
  147.             $stmt $this->stmt->execute($params);
  148.         } catch (Throwable $ex) {
  149.             if ($logger) {
  150.                 $logger->stopQuery();
  151.             }
  152.             $this->conn->handleExceptionDuringQuery($ex$this->sql$this->params$this->types);
  153.         }
  154.         if ($logger) {
  155.             $logger->stopQuery();
  156.         }
  157.         return $stmt;
  158.     }
  159.     /**
  160.      * Closes the cursor, freeing the database resources used by this statement.
  161.      *
  162.      * @deprecated Use Result::free() instead.
  163.      *
  164.      * @return bool TRUE on success, FALSE on failure.
  165.      */
  166.     public function closeCursor()
  167.     {
  168.         return $this->stmt->closeCursor();
  169.     }
  170.     /**
  171.      * Returns the number of columns in the result set.
  172.      *
  173.      * @return int
  174.      */
  175.     public function columnCount()
  176.     {
  177.         return $this->stmt->columnCount();
  178.     }
  179.     /**
  180.      * Fetches the SQLSTATE associated with the last operation on the statement.
  181.      *
  182.      * @deprecated The error information is available via exceptions.
  183.      *
  184.      * @return string|int|bool
  185.      */
  186.     public function errorCode()
  187.     {
  188.         return $this->stmt->errorCode();
  189.     }
  190.     /**
  191.      * {@inheritDoc}
  192.      *
  193.      * @deprecated The error information is available via exceptions.
  194.      */
  195.     public function errorInfo()
  196.     {
  197.         return $this->stmt->errorInfo();
  198.     }
  199.     /**
  200.      * {@inheritdoc}
  201.      *
  202.      * @deprecated Use one of the fetch- or iterate-related methods.
  203.      */
  204.     public function setFetchMode($fetchMode$arg2 null$arg3 null)
  205.     {
  206.         if ($arg2 === null) {
  207.             return $this->stmt->setFetchMode($fetchMode);
  208.         }
  209.         if ($arg3 === null) {
  210.             return $this->stmt->setFetchMode($fetchMode$arg2);
  211.         }
  212.         return $this->stmt->setFetchMode($fetchMode$arg2$arg3);
  213.     }
  214.     /**
  215.      * Required by interface IteratorAggregate.
  216.      *
  217.      * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
  218.      *
  219.      * {@inheritdoc}
  220.      */
  221.     public function getIterator()
  222.     {
  223.         return $this->stmt;
  224.     }
  225.     /**
  226.      * {@inheritdoc}
  227.      *
  228.      * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
  229.      */
  230.     public function fetch($fetchMode null$cursorOrientation PDO::FETCH_ORI_NEXT$cursorOffset 0)
  231.     {
  232.         return $this->stmt->fetch($fetchMode);
  233.     }
  234.     /**
  235.      * {@inheritdoc}
  236.      *
  237.      * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
  238.      */
  239.     public function fetchAll($fetchMode null$fetchArgument null$ctorArgs null)
  240.     {
  241.         if ($ctorArgs !== null) {
  242.             return $this->stmt->fetchAll($fetchMode$fetchArgument$ctorArgs);
  243.         }
  244.         if ($fetchArgument !== null) {
  245.             return $this->stmt->fetchAll($fetchMode$fetchArgument);
  246.         }
  247.         return $this->stmt->fetchAll($fetchMode);
  248.     }
  249.     /**
  250.      * {@inheritDoc}
  251.      *
  252.      * @deprecated Use fetchOne() instead.
  253.      */
  254.     public function fetchColumn($columnIndex 0)
  255.     {
  256.         return $this->stmt->fetchColumn($columnIndex);
  257.     }
  258.     /**
  259.      * {@inheritdoc}
  260.      *
  261.      * @throws Exception
  262.      */
  263.     public function fetchNumeric()
  264.     {
  265.         try {
  266.             if ($this->stmt instanceof Result) {
  267.                 return $this->stmt->fetchNumeric();
  268.             }
  269.             return $this->stmt->fetch(FetchMode::NUMERIC);
  270.         } catch (Exception $e) {
  271.             $this->conn->handleDriverException($e);
  272.         }
  273.     }
  274.     /**
  275.      * {@inheritdoc}
  276.      *
  277.      * @throws Exception
  278.      */
  279.     public function fetchAssociative()
  280.     {
  281.         try {
  282.             if ($this->stmt instanceof Result) {
  283.                 return $this->stmt->fetchAssociative();
  284.             }
  285.             return $this->stmt->fetch(FetchMode::ASSOCIATIVE);
  286.         } catch (Exception $e) {
  287.             $this->conn->handleDriverException($e);
  288.         }
  289.     }
  290.     /**
  291.      * {@inheritDoc}
  292.      *
  293.      * @throws Exception
  294.      */
  295.     public function fetchOne()
  296.     {
  297.         try {
  298.             if ($this->stmt instanceof Result) {
  299.                 return $this->stmt->fetchOne();
  300.             }
  301.             return $this->stmt->fetch(FetchMode::COLUMN);
  302.         } catch (Exception $e) {
  303.             $this->conn->handleDriverException($e);
  304.         }
  305.     }
  306.     /**
  307.      * {@inheritdoc}
  308.      *
  309.      * @throws Exception
  310.      */
  311.     public function fetchAllNumeric(): array
  312.     {
  313.         try {
  314.             if ($this->stmt instanceof Result) {
  315.                 return $this->stmt->fetchAllNumeric();
  316.             }
  317.             return $this->stmt->fetchAll(FetchMode::NUMERIC);
  318.         } catch (Exception $e) {
  319.             $this->conn->handleDriverException($e);
  320.         }
  321.     }
  322.     /**
  323.      * {@inheritdoc}
  324.      *
  325.      * @throws Exception
  326.      */
  327.     public function fetchAllAssociative(): array
  328.     {
  329.         try {
  330.             if ($this->stmt instanceof Result) {
  331.                 return $this->stmt->fetchAllAssociative();
  332.             }
  333.             return $this->stmt->fetchAll(FetchMode::ASSOCIATIVE);
  334.         } catch (Exception $e) {
  335.             $this->conn->handleDriverException($e);
  336.         }
  337.     }
  338.     /**
  339.      * Returns an associative array with the keys mapped to the first column and the values mapped to the second column.
  340.      *
  341.      * The result must contain at least two columns.
  342.      *
  343.      * @return array<mixed,mixed>
  344.      *
  345.      * @throws Exception
  346.      */
  347.     public function fetchAllKeyValue(): array
  348.     {
  349.         $this->ensureHasKeyValue();
  350.         $data = [];
  351.         foreach ($this->fetchAllNumeric() as [$key$value]) {
  352.             $data[$key] = $value;
  353.         }
  354.         return $data;
  355.     }
  356.     /**
  357.      * Returns an associative array with the keys mapped to the first column and the values being
  358.      * an associative array representing the rest of the columns and their values.
  359.      *
  360.      * @return array<mixed,array<string,mixed>>
  361.      *
  362.      * @throws Exception
  363.      */
  364.     public function fetchAllAssociativeIndexed(): array
  365.     {
  366.         $data = [];
  367.         foreach ($this->fetchAll(FetchMode::ASSOCIATIVE) as $row) {
  368.             $data[array_shift($row)] = $row;
  369.         }
  370.         return $data;
  371.     }
  372.     /**
  373.      * {@inheritdoc}
  374.      *
  375.      * @throws Exception
  376.      */
  377.     public function fetchFirstColumn(): array
  378.     {
  379.         try {
  380.             if ($this->stmt instanceof Result) {
  381.                 return $this->stmt->fetchFirstColumn();
  382.             }
  383.             return $this->stmt->fetchAll(FetchMode::COLUMN);
  384.         } catch (Exception $e) {
  385.             $this->conn->handleDriverException($e);
  386.         }
  387.     }
  388.     /**
  389.      * {@inheritDoc}
  390.      *
  391.      * @return Traversable<int,array<int,mixed>>
  392.      *
  393.      * @throws Exception
  394.      */
  395.     public function iterateNumeric(): Traversable
  396.     {
  397.         try {
  398.             if ($this->stmt instanceof Result) {
  399.                 while (($row $this->stmt->fetchNumeric()) !== false) {
  400.                     yield $row;
  401.                 }
  402.             } else {
  403.                 while (($row $this->stmt->fetch(FetchMode::NUMERIC)) !== false) {
  404.                     yield $row;
  405.                 }
  406.             }
  407.         } catch (Exception $e) {
  408.             $this->conn->handleDriverException($e);
  409.         }
  410.     }
  411.     /**
  412.      * {@inheritDoc}
  413.      *
  414.      * @return Traversable<int,array<string,mixed>>
  415.      *
  416.      * @throws Exception
  417.      */
  418.     public function iterateAssociative(): Traversable
  419.     {
  420.         try {
  421.             if ($this->stmt instanceof Result) {
  422.                 while (($row $this->stmt->fetchAssociative()) !== false) {
  423.                     yield $row;
  424.                 }
  425.             } else {
  426.                 while (($row $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
  427.                     yield $row;
  428.                 }
  429.             }
  430.         } catch (Exception $e) {
  431.             $this->conn->handleDriverException($e);
  432.         }
  433.     }
  434.     /**
  435.      * Returns an iterator over the result set with the keys mapped to the first column
  436.      * and the values mapped to the second column.
  437.      *
  438.      * The result must contain at least two columns.
  439.      *
  440.      * @return Traversable<mixed,mixed>
  441.      *
  442.      * @throws Exception
  443.      */
  444.     public function iterateKeyValue(): Traversable
  445.     {
  446.         $this->ensureHasKeyValue();
  447.         foreach ($this->iterateNumeric() as [$key$value]) {
  448.             yield $key => $value;
  449.         }
  450.     }
  451.     /**
  452.      * Returns an iterator over the result set with the keys mapped to the first column and the values being
  453.      * an associative array representing the rest of the columns and their values.
  454.      *
  455.      * @return Traversable<mixed,array<string,mixed>>
  456.      *
  457.      * @throws Exception
  458.      */
  459.     public function iterateAssociativeIndexed(): Traversable
  460.     {
  461.         while (($row $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
  462.             yield array_shift($row) => $row;
  463.         }
  464.     }
  465.     /**
  466.      * {@inheritDoc}
  467.      *
  468.      * @return Traversable<int,mixed>
  469.      *
  470.      * @throws Exception
  471.      */
  472.     public function iterateColumn(): Traversable
  473.     {
  474.         try {
  475.             if ($this->stmt instanceof Result) {
  476.                 while (($value $this->stmt->fetchOne()) !== false) {
  477.                     yield $value;
  478.                 }
  479.             } else {
  480.                 while (($value $this->stmt->fetch(FetchMode::COLUMN)) !== false) {
  481.                     yield $value;
  482.                 }
  483.             }
  484.         } catch (Exception $e) {
  485.             $this->conn->handleDriverException($e);
  486.         }
  487.     }
  488.     /**
  489.      * Returns the number of rows affected by the last execution of this statement.
  490.      *
  491.      * @return int The number of affected rows.
  492.      */
  493.     public function rowCount()
  494.     {
  495.         return $this->stmt->rowCount();
  496.     }
  497.     public function free(): void
  498.     {
  499.         if ($this->stmt instanceof Result) {
  500.             $this->stmt->free();
  501.             return;
  502.         }
  503.         $this->stmt->closeCursor();
  504.     }
  505.     /**
  506.      * Gets the wrapped driver statement.
  507.      *
  508.      * @return \Doctrine\DBAL\Driver\Statement
  509.      */
  510.     public function getWrappedStatement()
  511.     {
  512.         return $this->stmt;
  513.     }
  514.     private function ensureHasKeyValue(): void
  515.     {
  516.         $columnCount $this->columnCount();
  517.         if ($columnCount 2) {
  518.             throw NoKeyValue::fromColumnCount($columnCount);
  519.         }
  520.     }
  521. }