vendor\kafoso\doctrine-firebird-driver\src\Driver\FirebirdInterbase\Statement.php line 279

Open in your IDE?
  1. <?php
  2. namespace IST\DoctrineFirebirdDriver\Driver\FirebirdInterbase;
  3. use Doctrine\DBAL\Driver\Statement as StatementInterace;
  4. use Doctrine\DBAL\Driver\StatementIterator;
  5. /**
  6.  * Based on https://github.com/helicon-os/doctrine-dbal
  7.  */
  8. class Statement implements \IteratorAggregateStatementInterace
  9. {
  10.     /**
  11.      * @var Connection $connection
  12.      */
  13.     protected $connection;
  14.     /**
  15.      * @var null|resource   Resource of the prepared statement
  16.      */
  17.     protected $ibaseStatementRc;
  18.     /**
  19.      * @var null|resource  Query result resource
  20.      */
  21.     public $ibaseResultRc null;
  22.     /**
  23.      * The SQL or DDL statement.
  24.      * @var string
  25.      */
  26.     protected $statement null;
  27.     /**
  28.      * Zero-Based List of parameter bindings
  29.      * @var array
  30.      */
  31.     protected $queryParamBindings = [];
  32.     /**
  33.      * Zero-Based List of parameter binding types
  34.      * @var array
  35.      */
  36.     protected $queryParamTypes = [];
  37.     /**
  38.      * @var integer Default fetch mode set by setFetchMode
  39.      */
  40.     protected $defaultFetchMode = \PDO::FETCH_BOTH;
  41.     /**
  42.      * @var string  Default class to be used by FETCH_CLASS or FETCH_OBJ
  43.      */
  44.     protected $defaultFetchClass '\stdClass';
  45.     /**
  46.      * @var integer Default column to fetch by FETCH_COLUMN
  47.      */
  48.     protected $defaultFetchColumn 0;
  49.     /**
  50.      * @var array   Parameters to be passed to constructor in FETCH_CLASS
  51.      */
  52.     protected $defaultFetchClassConstructorArgs = [];
  53.     /**
  54.      * @var Object  Object used as target by FETCH_INTO
  55.      */
  56.     protected $defaultFetchInto null;
  57.     /**
  58.      * Number of rows affected by last execute
  59.      * @var integer
  60.      */
  61.     protected $affectedRows 0;
  62.     protected $numFields false;
  63.     /**
  64.      * Mapping between parameter names and positions
  65.      *
  66.      * The map is indexed by parameter name including the leading ':'.
  67.      *
  68.      * Each item contains an array of zero-based parameter positions.
  69.      */
  70.     protected $namedParamsMap = [];
  71.     /**
  72.      * @param string  $prepareString
  73.      *
  74.      * @throws Exception
  75.      */
  76.     public function __construct(Connection $connection$prepareString)
  77.     {
  78.         $this->connection $connection;
  79.         $this->setStatement($prepareString);
  80.     }
  81.     /**
  82.      * Frees the ressources
  83.      */
  84.     public function __destruct()
  85.     {
  86.         $this->closeCursor();
  87.         if ($this->ibaseStatementRc && is_resource($this->ibaseStatementRc)) {
  88.             $success = @ibase_free_query($this->ibaseStatementRc);
  89.             if (false == $success) {
  90.                 $this->checkLastApiCall();
  91.             }
  92.             $this->ibaseStatementRc null;
  93.         }
  94.     }
  95.     /**
  96.      * {@inheritdoc}
  97.      */
  98.     public function errorCode()
  99.     {
  100.         return ibase_errcode();
  101.     }
  102.     /**
  103.      * {@inheritdoc}
  104.      */
  105.     public function errorInfo()
  106.     {
  107.         $errorCode $this->errorCode();
  108.         if ($errorCode) {
  109.             return [
  110.                 'code' => $this->errorCode(),
  111.                 'message' => ibase_errmsg(),
  112.             ];
  113.         }
  114.         return [
  115.             'code' => 0,
  116.             'message' => null,
  117.         ];
  118.     }
  119.     /**
  120.      * Checks ibase_error and raises an exception if an error occured
  121.      */
  122.     protected function checkLastApiCall()
  123.     {
  124.         $lastError $this->errorInfo();
  125.         if (isset($lastError['code']) && $lastError['code']) {
  126.             throw Exception::fromErrorInfo($lastError);
  127.         }
  128.     }
  129.     /**
  130.      * Creates an object and tries to set the object properties in a case insensitive way
  131.      *
  132.      * If a object is passed, the constructor is not called again
  133.      *
  134.      * NOTE: This function tries to mimic PDO's behaviour to set the properties *before* calling the constructor if possible.
  135.      *
  136.      * @param string|object $aClassOrObject Class name. If a object instance is passed, the given object is used
  137.      * @param array $aConstructorArgArray Parameters passed to the constructor
  138.      * @param array $aPropertyList Associative array of object properties
  139.      * @return object created object or object passed in $aClassOrObject
  140.      */
  141.     protected function createObjectAndSetPropertiesCaseInsenstive($aClassOrObject, array $aConstructorArgArray, array $aPropertyList)
  142.     {
  143.         $callConstructor false;
  144.         if (is_object($aClassOrObject)) {
  145.             $result $aClassOrObject;
  146.             $reflector = new \ReflectionObject($aClassOrObject);
  147.         } else {
  148.             if (!is_string($aClassOrObject))
  149.                 $aClassOrObject '\stdClass';
  150.             $classReflector = new \ReflectionClass($aClassOrObject);
  151.             if (method_exists($classReflector'newInstanceWithoutConstructor')) {
  152.                 $result $classReflector->newInstanceWithoutConstructor();
  153.                 $callConstructor true;
  154.             } else {
  155.                 $result $classReflector->newInstance($aConstructorArgArray);
  156.             }
  157.             $reflector = new \ReflectionObject($result);
  158.         }
  159.         $propertyReflections $reflector->getProperties();
  160.         foreach ($aPropertyList as $properyName => $propertyValue) {
  161.             $createNewProperty true;
  162.             foreach ($propertyReflections as $propertyReflector/* @var $propertyReflector \ReflectionProperty */ {
  163.                 if (strcasecmp($properyName$propertyReflector->name) == 0) {
  164.                     $propertyReflector->setValue($result$propertyValue);
  165.                     $createNewProperty false;
  166.                     break; // ===> BREAK We have found what we want
  167.                 }
  168.             }
  169.             if ($createNewProperty) {
  170.                 $result->$properyName $propertyValue;
  171.             }
  172.         }
  173.         if ($callConstructor) {
  174.             $constructorRefelector $reflector->getConstructor();
  175.             if ($constructorRefelector) {
  176.                 $constructorRefelector->invokeArgs($result$aConstructorArgArray);
  177.             }
  178.         }
  179.         return $result;
  180.     }
  181.     /**
  182.      * {@inheritDoc}
  183.      * @param int $fetchMode
  184.      * @param mixed $arg2
  185.      * @param mixed $arg3
  186.      */
  187.     public function setFetchMode($fetchMode$arg2 null$arg3 null)
  188.     {
  189.         switch ($fetchMode) {
  190.             case \PDO::FETCH_OBJ:
  191.             case \PDO::FETCH_CLASS: {
  192.                     $this->defaultFetchMode $fetchMode;
  193.                     $this->defaultFetchClass is_string($arg2) ? $arg2 '\stdClass';
  194.                     $this->defaultFetchClassConstructorArgs is_array($arg3) ? $arg3 : [];
  195.                     break;
  196.                 }
  197.             case \PDO::FETCH_INTO: {
  198.                     $this->defaultFetchMode $fetchMode;
  199.                     $this->defaultFetchInfo $arg2;
  200.                 }
  201.             case \PDO::FETCH_COLUMN: {
  202.                     $this->defaultFetchMode $fetchMode;
  203.                     $this->defaultFetchColumn = isset($arg2) ? $arg2 0;
  204.                 }
  205.             default:
  206.                 $this->defaultFetchMode $fetchMode;
  207.         }
  208.     }
  209.     /**
  210.      * {@inheritdoc}
  211.      */
  212.     public function bindValue($param$value$type null)
  213.     {
  214.         return $this->bindParam($param$value$typenull);
  215.     }
  216.     /**
  217.      * {@inheritdoc}
  218.      */
  219.     public function bindParam($column, &$variable$type null$length null)
  220.     {
  221.         if (is_object($variable)) {
  222.             $variable = (string) $variable;
  223.         }
  224.         if (is_numeric($column)) {
  225.             $this->queryParamBindings[$column 1] = &$variable;
  226.             $this->queryParamTypes[$column 1] = $type;
  227.         } else {
  228.             if (isset($this->namedParamsMap[$column])) {
  229.                 foreach ($this->namedParamsMap[$column] as $pp/* @var integer $pp *zero* based Parameter index */ {
  230.                     $this->queryParamBindings[$pp] = &$variable;
  231.                     $this->queryParamTypes[$pp] = $type;
  232.                 }
  233.             } else {
  234.                 throw new Exception('Cannot bind to unknown parameter ' $columnnull);
  235.             }
  236.         }
  237.     }
  238.     /**
  239.      * {@inheritdoc}
  240.      */
  241.     public function closeCursor()
  242.     {
  243.         if ($this->ibaseResultRc && is_resource($this->ibaseResultRc)) {
  244.             $success = @ibase_free_result($this->ibaseResultRc);
  245.             if (false == $success) {
  246.                 $this->checkLastApiCall();
  247.             }
  248.         }
  249.         $this->ibaseResultRc null;
  250.     }
  251.     /**
  252.      * {@inheritdoc}
  253.      */
  254.     public function columnCount()
  255.     {
  256.         return $this->numFields;
  257.     }
  258.     /**
  259.      * {@inheritdoc}
  260.      */
  261.     public function execute($params null)
  262.     {
  263.         $this->affectedRows 0;
  264.         // Bind passed parameters
  265.         if (is_array($params) && !empty($params)) {
  266.             $idxShift array_key_exists(0$params) ? 0;
  267.             $hasZeroIndex array_key_exists(0$params);
  268.             foreach ($params as $key => $val) {
  269.                 $key = (is_numeric($key)) ? $key $idxShift $key;
  270.                 $this->bindValue($key$val);
  271.             }
  272.         }
  273.         // Execute statement
  274.         if (count($this->queryParamBindings) > 0) {
  275.            $this->ibaseResultRc $this->doExecPrepared();
  276.         } else {
  277.             $this->ibaseResultRc $this->doDirectExec();
  278.         }
  279.         // Check result
  280.         if ($this->ibaseResultRc !== false) {
  281.             // Result seems ok - is either #rows or result handle
  282.             if (is_numeric($this->ibaseResultRc)) {
  283.                 $this->affectedRows $this->ibaseResultRc;
  284.                 $this->numFields = @ibase_num_fields($this->ibaseResultRc);
  285.                 $this->ibaseResultRc null;
  286.             } elseif (is_resource($this->ibaseResultRc)) {
  287.                 $this->affectedRows = @ibase_affected_rows($this->connection->getActiveTransaction());
  288.                 $this->numFields = @ibase_num_fields($this->ibaseResultRc);
  289.             }
  290.             // As the ibase-api does not have an auto-commit-mode, autocommit is simulated by calling the
  291.             // function autoCommit of the connection
  292.             $this->connection->autoCommit();
  293.         } else {
  294.             // Error
  295.             $this->checkLastApiCall();
  296.         }
  297.         if ($this->ibaseResultRc === false) {
  298.             $this->ibaseResultRc null;
  299.             return false;
  300.         } else {
  301.             return true;
  302.         }
  303.     }
  304.     /**
  305.      * {@inheritdoc}
  306.      */
  307.     public function getIterator()
  308.     {
  309.         $data $this->fetchAll();
  310.         return new \ArrayIterator($data);
  311.     }
  312.     /**
  313.      * Fetches a single row into a object
  314.      *
  315.      * @param object|string $fetchIntoObjectOrClass Object class to create or object to update
  316.      *
  317.      * @return boolean
  318.      */
  319.     public function fetchObject($fetchIntoObjectOrClass '\stdClass')
  320.     {
  321.         return $this->internalFetchClassOrObject($fetchIntoObjectOrClass);
  322.     }
  323.     /**
  324.      * {@inheritdoc}
  325.      * @param int $fetchMode
  326.      * @param null|string $optArg1
  327.      * @return mixed
  328.      */
  329.     // public function fetch($fetchMode = null, $optArg1 = null)
  330.     public function fetch($fetchMode NULL$cursorOrientation = \PDO::FETCH_ORI_NEXT$cursorOffset 0)
  331.     {
  332.         $optArg1 null;
  333.         $fetchMode !== null || $fetchMode $this->defaultFetchMode;
  334.         switch ($fetchMode) {
  335.             case \PDO::FETCH_OBJ:
  336.             case \PDO::FETCH_CLASS:
  337.                 return $this->internalFetchClassOrObject(isset($optArg1) ? $optArg1 $this->defaultFetchClass$this->defaultFetchClassConstructorArgs);
  338.             case \PDO::FETCH_INTO:
  339.                 return $this->internalFetchClassOrObject(isset($optArg1) ? $optArg1 $this->defaultFetchInto, []);
  340.             case \PDO::FETCH_ASSOC:
  341.                 return $this->internalFetchAssoc();
  342.                 break;
  343.             case \PDO::FETCH_NUM:
  344.                 return $this->internalFetchNum();
  345.                 break;
  346.             case \PDO::FETCH_BOTH:
  347.                 return $this->internalFetchBoth();
  348.             default:
  349.                 throw new Exception('Fetch mode ' $fetchMode ' not supported by this driver in ' __METHOD__);
  350.         }
  351.     }
  352.     /**
  353.      * {@inheritDoc}
  354.      */
  355.     public function fetchAll($fetchMode null$fetchArgument null$ctorArgs null)
  356.     {
  357.         $fetchMode !== null || $fetchMode $this->defaultFetchMode;
  358.         switch ($fetchMode) {
  359.             case \PDO::FETCH_CLASS:
  360.             case \PDO::FETCH_OBJ: {
  361.                     return $this->internalFetchAllClassOrObjects(
  362.                                     $fetchArgument == null $this->defaultFetchClass $fetchArgument$ctorArgs == null $this->defaultFetchClassConstructorArgs $ctorArgs);
  363.                     break;
  364.                 }
  365.             case \PDO::FETCH_COLUMN: {
  366.                     return $this->internalFetchAllColumn(
  367.                                     $fetchArgument == null $fetchArgument);
  368.                     break;
  369.                 }
  370.             case \PDO::FETCH_BOTH: {
  371.                     return $this->internalFetchAllBoth();
  372.                 }
  373.             case \PDO::FETCH_ASSOC: {
  374.                     return $this->internalFetchAllAssoc();
  375.                 }
  376.             case \PDO::FETCH_NUM: {
  377.                     return $this->internalFetchAllNum();
  378.                 }
  379.             default: {
  380.                     throw new Exception('Fetch mode ' $fetchMode ' not supported by this driver in ' __METHOD__);
  381.                 }
  382.         }
  383.     }
  384.     /**
  385.      * {@inheritdoc}
  386.      */
  387.     public function fetchColumn($columnIndex 0)
  388.     {
  389.         return $this->internalFetchColumn($columnIndex);
  390.     }
  391.     /**
  392.      * {@inheritDoc}
  393.      */
  394.     public function rowCount()
  395.     {
  396.         return $this->affectedRows;
  397.     }
  398.     /**
  399.      * Fetches a single column.
  400.      * @param int $columnIndex
  401.      * @return bool|mixed
  402.      */
  403.     protected function internalFetchColumn($columnIndex 0)
  404.     {
  405.         $rowData $this->internalFetchNum();
  406.         if (is_array($rowData)) {
  407.             return (isset($rowData[$columnIndex]) ? $rowData[$columnIndex] : NULL);
  408.         } else {
  409.             return FALSE;
  410.         }
  411.     }
  412.     /**
  413.      * @param int $columnIndex
  414.      * @return array
  415.      */
  416.     protected function internalFetchAllColumn($columnIndex 0)
  417.     {
  418.         $result = [];
  419.         while ($data $this->internalFetchColumn()) {
  420.             $result[] = $data;
  421.         }
  422.         return $result;
  423.     }
  424.     /**
  425.      * Fetch associative array
  426.      * @return array
  427.      */
  428.     protected function internalFetchAllAssoc()
  429.     {
  430.         $result = [];
  431.         while ($data $this->internalFetchAssoc()) {
  432.             $result[] = $data;
  433.         }
  434.         return $result;
  435.     }
  436.     /**
  437.      * Fetches a record into a array
  438.      * @return bool|array
  439.      */
  440.     protected function internalFetchNum()
  441.     {
  442.         return @ibase_fetch_row($this->ibaseResultRcIBASE_TEXT);
  443.     }
  444.     /**
  445.      * Fetch all records into an array of numeric arrays
  446.      * @return array
  447.      */
  448.     protected function internalFetchAllNum()
  449.     {
  450.         $result = [];
  451.         while ($data $this->internalFetchNum()) {
  452.             $result[] = $data;
  453.         }
  454.         return $result;
  455.     }
  456.     /**
  457.      * Fetches all records into an array containing arrays with column name and column index
  458.      * @return array
  459.      */
  460.     protected function internalFetchAllBoth()
  461.     {
  462.         $result = [];
  463.         while ($data $this->internalFetchBoth()) {
  464.             $result[] = $data;
  465.         }
  466.         return $result;
  467.     }
  468.     /**
  469.      * Fetches into an object
  470.      *
  471.      * @param object|string $aClassOrObject Object instance or class
  472.      * @param array $constructorArguments   Parameters to pass to the constructor
  473.      * @return object|false                 Result object or false
  474.      */
  475.     protected function internalFetchClassOrObject($aClassOrObject, array $constructorArguments null)
  476.     {
  477.         $rowData $this->internalFetchAssoc();
  478.         if (is_array($rowData)) {
  479.             return $this->createObjectAndSetPropertiesCaseInsenstive($aClassOrObjectis_array($constructorArguments) ? $constructorArguments : [], $rowData);
  480.         } else {
  481.             return $rowData;
  482.         }
  483.     }
  484.     /**
  485.      * Fetches all records into objects
  486.      *
  487.      * @param object|string $aClassOrObject Object instance or class
  488.      * @param array $constructorArguments   Parameters to pass to the constructor
  489.      * @return object|false                 Result object or false
  490.      */
  491.     protected function internalFetchAllClassOrObjects($aClassOrObject, array $constructorArguments)
  492.     {
  493.         $result = [];
  494.         while ($row $this->internalFetchClassOrObject($aClassOrObject$constructorArguments)) {
  495.             if ($row !== false) {
  496.                 $result[] = $row;
  497.             }
  498.         }
  499.         return $result;
  500.     }
  501.     /**
  502.      * Fetches the next record into an associative array
  503.      * @return array
  504.      */
  505.     protected function internalFetchAssoc()
  506.     {
  507.         return @ibase_fetch_assoc($this->ibaseResultRcIBASE_TEXT);
  508.     }
  509.     /**
  510.      * Fetches the next record into an array using column name and index as key
  511.      * @return array|boolean
  512.      */
  513.     protected function internalFetchBoth()
  514.     {
  515.         $tmpData = @ibase_fetch_assoc($this->ibaseResultRcIBASE_TEXT);
  516.         if (false !== $tmpData) {
  517.             return array_merge(array_values($tmpData), $tmpData);
  518.         }
  519.         return false;
  520.     }
  521.     /**
  522.      * Prepares the statement for further use and executes it
  523.      * @result resource|boolean
  524.      */
  525.     protected function doDirectExec()
  526.     {
  527.         $callArgs $this->queryParamBindings;
  528.         array_unshift($callArgs$this->connection->getActiveTransaction(), $this->statement);
  529.         return @call_user_func_array('ibase_query'$callArgs);
  530.     }
  531.     /**
  532.      * Prepares the statement for further use and executes it
  533.      * @result resource|boolean
  534.      */
  535.     protected function doExecPrepared()
  536.     {
  537.         if (!$this->ibaseStatementRc || !is_resource($this->ibaseStatementRc)) {
  538.             $this->ibaseStatementRc = @ibase_prepare(
  539.                 $this->connection->getActiveTransaction(),
  540.                 $this->statement
  541.             );
  542.             if (!$this->ibaseStatementRc || !is_resource($this->ibaseStatementRc))
  543.                 $this->checkLastApiCall();
  544.         }
  545.         $callArgs $this->queryParamBindings;
  546.         array_unshift($callArgs$this->ibaseStatementRc);
  547.         return @call_user_func_array('ibase_execute'$callArgs);
  548.     }
  549.     /**
  550.      * Sets and analyzes the statement
  551.      *
  552.      * @param string $statement
  553.      */
  554.     protected function setStatement($statement)
  555.     {
  556.         $this->statement $statement;
  557.         $this->namedParamsMap = [];
  558.         $pp = \Doctrine\DBAL\SQLParserUtils::getPlaceholderPositions($statementfalse);
  559.         if (!empty($pp)) {
  560.             $pidx 0// index-position of the parameter
  561.             $le 0// substr start position
  562.             $convertedStatement '';
  563.             foreach ($pp as $ppos => $pname) {
  564.                 $convertedStatement .= substr($statement$le$ppos $le) . '?';
  565.                 if (!isset($this->namedParamsMap[':' $pname])) {
  566.                     $this->namedParamsMap[':' $pname] = (array)$pidx;
  567.                 } else {
  568.                     $this->namedParamsMap[':' $pname][] = $pidx;
  569.                 }
  570.                 $le $ppos strlen($pname) + 1// Continue at position after :name
  571.                 $pidx++;
  572.             }
  573.             $convertedStatement .= substr($statement$le);
  574.             $this->statement $convertedStatement;
  575.         }
  576.     }
  577. }