Browse Source

Add typing to Database/Statement

I had to widen the types on fetch() and fetchAll() as we not only take
the CakePHP constants, but also PDO constants which are integers.
Mark Story 7 years ago
parent
commit
fef1e25ccb

+ 2 - 2
src/Database/Log/LoggingStatement.php

@@ -46,7 +46,7 @@ class LoggingStatement extends StatementDecorator
      * @return bool True on success, false otherwise
      * @throws \Exception Re-throws any exception raised during query execution.
      */
-    public function execute($params = null)
+    public function execute(?array $params = null): bool
     {
         $t = microtime(true);
         $query = new LoggedQuery();
@@ -92,7 +92,7 @@ class LoggingStatement extends StatementDecorator
      * @param string|int|null $type PDO type or name of configured Type class
      * @return void
      */
-    public function bindValue($column, $value, $type = 'string')
+    public function bindValue($column, $value, $type = 'string'): void
     {
         parent::bindValue($column, $value, $type);
         if ($type === null) {

+ 2 - 1
src/Database/Statement/BufferResultsTrait.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -34,7 +35,7 @@ trait BufferResultsTrait
      * @param bool $buffer Toggle buffering
      * @return $this
      */
-    public function bufferResults($buffer)
+    public function bufferResults(bool $buffer)
     {
         $this->_bufferResults = (bool)$buffer;
 

+ 19 - 18
src/Database/Statement/BufferedStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -76,7 +77,7 @@ class BufferedStatement implements Iterator, StatementInterface
      * @param \Cake\Database\StatementInterface $statement Statement implementation such as PDOStatement
      * @param \Cake\Database\Driver $driver Driver instance
      */
-    public function __construct(StatementInterface $statement, $driver)
+    public function __construct(StatementInterface $statement, \Cake\Database\Driver $driver)
     {
         $this->statement = $statement;
         $this->_driver = $driver;
@@ -88,7 +89,7 @@ class BufferedStatement implements Iterator, StatementInterface
      * @param string $property internal property to get
      * @return mixed
      */
-    public function __get($property)
+    public function __get(string $property)
     {
         if ($property === 'queryString') {
             return $this->statement->queryString;
@@ -98,7 +99,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function bindValue($column, $value, $type = 'string')
+    public function bindValue($column, $value, string $type = 'string'): void
     {
         $this->statement->bindValue($column, $value, $type);
     }
@@ -106,7 +107,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function closeCursor()
+    public function closeCursor(): void
     {
         $this->statement->closeCursor();
     }
@@ -114,7 +115,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function columnCount()
+    public function columnCount(): int
     {
         return $this->statement->columnCount();
     }
@@ -130,7 +131,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function errorInfo()
+    public function errorInfo(): array
     {
         return $this->statement->errorInfo();
     }
@@ -138,7 +139,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function execute($params = null)
+    public function execute(?array $params = null): bool
     {
         $this->_reset();
         $this->_hasExecuted = true;
@@ -149,7 +150,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function fetchColumn($position)
+    public function fetchColumn(int $position)
     {
         $result = $this->fetch(static::FETCH_TYPE_NUM);
         if (isset($result[$position])) {
@@ -165,7 +166,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return int
      */
-    public function count()
+    public function count(): int
     {
         return $this->rowCount();
     }
@@ -173,7 +174,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function bind($params, $types)
+    public function bind(array $params, array $types): void
     {
         $this->statement->bind($params, $types);
     }
@@ -181,7 +182,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function lastInsertId($table = null, $column = null)
+    public function lastInsertId(?string $table = null, ?string $column = null)
     {
         return $this->statement->lastInsertId($table, $column);
     }
@@ -192,7 +193,7 @@ class BufferedStatement implements Iterator, StatementInterface
      * @param string $type The type to fetch.
      * @return array|false
      */
-    public function fetch($type = self::FETCH_TYPE_NUM)
+    public function fetch(string $type = self::FETCH_TYPE_NUM)
     {
         if ($this->_allFetched) {
             $row = false;
@@ -254,7 +255,7 @@ class BufferedStatement implements Iterator, StatementInterface
     /**
      * {@inheritDoc}
      */
-    public function rowCount()
+    public function rowCount(): int
     {
         if (!$this->_allFetched) {
             $this->fetchAll(static::FETCH_TYPE_ASSOC);
@@ -268,7 +269,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return void
      */
-    protected function _reset()
+    protected function _reset(): void
     {
         $this->buffer = [];
         $this->_allFetched = false;
@@ -300,7 +301,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return void
      */
-    public function rewind()
+    public function rewind(): void
     {
         $this->index = 0;
     }
@@ -310,7 +311,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return bool
      */
-    public function valid()
+    public function valid(): bool
     {
         $old = $this->index;
         $row = $this->fetch(self::FETCH_TYPE_ASSOC);
@@ -327,7 +328,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return void
      */
-    public function next()
+    public function next(): void
     {
         $this->index += 1;
     }
@@ -337,7 +338,7 @@ class BufferedStatement implements Iterator, StatementInterface
      *
      * @return \Cake\Database\StatementInterface
      */
-    public function getInnerStatement()
+    public function getInnerStatement(): \Cake\Database\StatementInterface
     {
         return $this->statement;
     }

+ 5 - 4
src/Database/Statement/CallbackStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -36,7 +37,7 @@ class CallbackStatement extends StatementDecorator
      * @param \Cake\Database\Driver $driver The driver instance used by the statement.
      * @param callable $callback The callback to apply to results before they are returned.
      */
-    public function __construct($statement, $driver, $callback)
+    public function __construct(\Cake\Database\StatementInterface $statement, \Cake\Database\Driver $driver, callable $callback)
     {
         parent::__construct($statement, $driver);
         $this->_callback = $callback;
@@ -47,7 +48,7 @@ class CallbackStatement extends StatementDecorator
      *
      * The result will be processed by the callback when it is not `false`.
      *
-     * @param string $type Either 'num' or 'assoc' to indicate the result format you would like.
+     * @param string|int $type Either 'num' or 'assoc' to indicate the result format you would like.
      * @return array|false
      */
     public function fetch($type = parent::FETCH_TYPE_NUM)
@@ -63,10 +64,10 @@ class CallbackStatement extends StatementDecorator
      *
      * Each row in the result will be processed by the callback when it is not `false.
      *
-     * @param string $type Either 'num' or 'assoc' to indicate the result format you would like.
+     * @param string|int $type Either 'num' or 'assoc' to indicate the result format you would like.
      * @return array
      */
-    public function fetchAll($type = parent::FETCH_TYPE_NUM)
+    public function fetchAll($type = parent::FETCH_TYPE_NUM): array
     {
         return array_map($this->_callback, $this->_statement->fetchAll($type));
     }

+ 1 - 0
src/Database/Statement/MysqlStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)

+ 5 - 4
src/Database/Statement/PDOStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -29,7 +30,7 @@ class PDOStatement extends StatementDecorator
      * @param \PDOStatement|null $statement Original statement to be decorated.
      * @param \Cake\Database\Driver|null $driver Driver instance.
      */
-    public function __construct(?Statement $statement = null, $driver = null)
+    public function __construct(?Statement $statement = null, ?\Cake\Database\Driver $driver = null)
     {
         parent::__construct($statement, $driver);
     }
@@ -59,7 +60,7 @@ class PDOStatement extends StatementDecorator
      * @param string|int $type PDO type or name of configured Type class
      * @return void
      */
-    public function bindValue($column, $value, $type = 'string')
+    public function bindValue($column, $value, $type = 'string'): void
     {
         if ($type === null) {
             $type = 'string';
@@ -83,7 +84,7 @@ class PDOStatement extends StatementDecorator
      *  print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
      * ```
      *
-     * @param string $type 'num' for positional columns, assoc for named columns
+     * @param string|int $type 'num' for positional columns, assoc for named columns
      * @return array|false Result array containing columns and values or false if no results
      * are left
      */
@@ -113,7 +114,7 @@ class PDOStatement extends StatementDecorator
      *  print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
      * ```
      *
-     * @param string $type num for fetching columns as positional keys or assoc for column names as keys
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
      * @return array|false list of all results from database for this statement, false on failure
      */
     public function fetchAll($type = parent::FETCH_TYPE_NUM)

+ 6 - 3
src/Database/Statement/SqliteStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -27,7 +28,7 @@ class SqliteStatement extends StatementDecorator
      * {@inheritDoc}
      *
      */
-    public function execute($params = null)
+    public function execute(?array $params = null): bool
     {
         if ($this->_statement instanceof BufferedStatement) {
             $this->_statement = $this->_statement->getInnerStatement();
@@ -45,9 +46,11 @@ class SqliteStatement extends StatementDecorator
      *
      * @return int
      */
-    public function rowCount()
+    public function rowCount(): int
     {
-        if (preg_match('/^(?:DELETE|UPDATE|INSERT)/i', $this->_statement->queryString)) {
+        if ($this->_statement->queryString &&
+            preg_match('/^(?:DELETE|UPDATE|INSERT)/i', $this->_statement->queryString)
+        ) {
             $changes = $this->_driver->prepare('SELECT CHANGES()');
             $changes->execute();
             $count = $changes->fetch()[0];

+ 1 - 0
src/Database/Statement/SqlserverStatement.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)

+ 15 - 14
src/Database/Statement/StatementDecorator.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -74,7 +75,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param string $property internal property to get
      * @return mixed
      */
-    public function __get($property)
+    public function __get(string $property)
     {
         if ($property === 'queryString') {
             return $this->_statement->queryString;
@@ -101,7 +102,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param string $type name of configured Type class
      * @return void
      */
-    public function bindValue($column, $value, $type = 'string')
+    public function bindValue($column, $value, $type = 'string'): void
     {
         $this->_statement->bindValue($column, $value, $type);
     }
@@ -113,7 +114,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return void
      */
-    public function closeCursor()
+    public function closeCursor(): void
     {
         $this->_statement->closeCursor();
     }
@@ -131,7 +132,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return int
      */
-    public function columnCount()
+    public function columnCount(): int
     {
         return $this->_statement->columnCount();
     }
@@ -152,7 +153,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return array
      */
-    public function errorInfo()
+    public function errorInfo(): array
     {
         return $this->_statement->errorInfo();
     }
@@ -166,7 +167,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param array|null $params list of values to be bound to query
      * @return bool true on success, false otherwise
      */
-    public function execute($params = null)
+    public function execute(?array $params = null): bool
     {
         $this->_hasExecuted = true;
 
@@ -186,7 +187,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
      * ```
      *
-     * @param string $type 'num' for positional columns, assoc for named columns
+     * @param string|int $type 'num' for positional columns, assoc for named columns
      * @return array|false Result array containing columns and values or false if no results
      * are left
      */
@@ -201,7 +202,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return array Result array containing columns and values an an associative array or an empty array if no results
      */
-    public function fetchAssoc()
+    public function fetchAssoc(): array
     {
         $result = $this->fetch(static::FETCH_TYPE_ASSOC);
 
@@ -214,7 +215,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param int $position The numeric position of the column to retrieve in the result
      * @return mixed|false Returns the specific value of the column designated at $position
      */
-    public function fetchColumn($position)
+    public function fetchColumn(int $position)
     {
         $result = $this->fetch(static::FETCH_TYPE_NUM);
         if (isset($result[$position])) {
@@ -235,7 +236,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
      * ```
      *
-     * @param string $type num for fetching columns as positional keys or assoc for column names as keys
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
      * @return array List of all results from database for this statement
      */
     public function fetchAll($type = self::FETCH_TYPE_NUM)
@@ -256,7 +257,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return int
      */
-    public function rowCount()
+    public function rowCount(): int
     {
         return $this->_statement->rowCount();
     }
@@ -291,7 +292,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      *
      * @return int
      */
-    public function count()
+    public function count(): int
     {
         return $this->rowCount();
     }
@@ -303,7 +304,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param array $types list of types to be used, keys should match those in $params
      * @return void
      */
-    public function bind($params, $types)
+    public function bind(array $params, array $types): void
     {
         if (empty($params)) {
             return;
@@ -330,7 +331,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param string|null $column the name of the column representing the primary key
      * @return string|int
      */
-    public function lastInsertId($table = null, $column = null)
+    public function lastInsertId(?string $table = null, ?string $column = null)
     {
         $row = null;
         if ($column && $this->columnCount()) {

+ 13 - 13
src/Database/StatementInterface.php

@@ -63,7 +63,7 @@ interface StatementInterface
      * @param string $type name of configured Type class
      * @return void
      */
-    public function bindValue($column, $value, $type = 'string');
+    public function bindValue($column, $value, string $type = 'string'): void;
 
     /**
      * Closes a cursor in the database, freeing up any resources and memory
@@ -72,7 +72,7 @@ interface StatementInterface
      *
      * @return void
      */
-    public function closeCursor();
+    public function closeCursor(): void;
 
     /**
      * Returns the number of columns this statement's results will contain
@@ -87,7 +87,7 @@ interface StatementInterface
      *
      * @return int
      */
-    public function columnCount();
+    public function columnCount(): int;
 
     /**
      * Returns the error code for the last error that occurred when executing this statement
@@ -102,7 +102,7 @@ interface StatementInterface
      *
      * @return array
      */
-    public function errorInfo();
+    public function errorInfo(): array;
 
     /**
      * Executes the statement by sending the SQL query to the database. It can optionally
@@ -113,7 +113,7 @@ interface StatementInterface
      * @param array|null $params list of values to be bound to query
      * @return bool true on success, false otherwise
      */
-    public function execute($params = null);
+    public function execute(?array $params = null): bool;
 
     /**
      * Returns the next row for the result set after executing this statement.
@@ -132,7 +132,7 @@ interface StatementInterface
      * @return array|false Result array containing columns and values or false if no results
      * are left
      */
-    public function fetch($type = 'num');
+    public function fetch(string $type = 'num');
 
     /**
      * Returns an array with all rows resulting from executing this statement
@@ -145,8 +145,8 @@ interface StatementInterface
      *  print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
      * ```
      *
-     * @param string $type num for fetching columns as positional keys or assoc for column names as keys
-     * @return array list of all results from database for this statement
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
+     * @return array|false list of all results from database for this statement or false on failure.
      */
     public function fetchAll($type = 'num');
 
@@ -163,7 +163,7 @@ interface StatementInterface
      *
      * @return int
      */
-    public function rowCount();
+    public function rowCount(): int;
 
     /**
      * Statements can be passed as argument for count()
@@ -171,7 +171,7 @@ interface StatementInterface
      *
      * @return int
      */
-    public function count();
+    public function count(): int;
 
     /**
      * Binds a set of values to statement object with corresponding type
@@ -180,14 +180,14 @@ interface StatementInterface
      * @param array $types list of types to be used, keys should match those in $params
      * @return void
      */
-    public function bind($params, $types);
+    public function bind(array $params, array $types): void;
 
     /**
      * Returns the latest primary inserted using this statement
      *
      * @param string|null $table table name or sequence to get last insert value from
      * @param string|null $column the name of the column representing the primary key
-     * @return string
+     * @return string|int
      */
-    public function lastInsertId($table = null, $column = null);
+    public function lastInsertId(?string $table = null, ?string $column = null);
 }

+ 11 - 4
tests/TestCase/Database/Log/LoggingStatementTest.php

@@ -30,7 +30,9 @@ class LoggingStatementTest extends TestCase
     public function testExecuteNoParams()
     {
         $inner = $this->getMockBuilder('PDOStatement')->getMock();
-        $inner->expects($this->once())->method('rowCount')->will($this->returnValue(3));
+        $inner->method('rowCount')->will($this->returnValue(3));
+        $inner->method('execute')->will($this->returnValue(true));
+
         $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->getMock();
         $logger->expects($this->once())
             ->method('log')
@@ -55,7 +57,9 @@ class LoggingStatementTest extends TestCase
     public function testExecuteWithParams()
     {
         $inner = $this->getMockBuilder('PDOStatement')->getMock();
-        $inner->expects($this->once())->method('rowCount')->will($this->returnValue(4));
+        $inner->method('rowCount')->will($this->returnValue(4));
+        $inner->method('execute')->will($this->returnValue(true));
+
         $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->getMock();
         $logger->expects($this->once())
             ->method('log')
@@ -80,7 +84,9 @@ class LoggingStatementTest extends TestCase
     public function testExecuteWithBinding()
     {
         $inner = $this->getMockBuilder('PDOStatement')->getMock();
-        $inner->expects($this->any())->method('rowCount')->will($this->returnValue(4));
+        $inner->method('rowCount')->will($this->returnValue(4));
+        $inner->method('execute')->will($this->returnValue(true));
+
         $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->getMock();
         $logger->expects($this->at(0))
             ->method('log')
@@ -125,7 +131,8 @@ class LoggingStatementTest extends TestCase
         $this->expectExceptionMessage('This is bad');
         $exception = new \LogicException('This is bad');
         $inner = $this->getMockBuilder('PDOStatement')->getMock();
-        $inner->expects($this->once())->method('execute')
+        $inner->expects($this->once())
+            ->method('execute')
             ->will($this->throwException($exception));
         $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->getMock();
         $logger->expects($this->once())

+ 2 - 0
tests/TestCase/Database/Schema/SqliteSchemaTest.php

@@ -1033,6 +1033,7 @@ SQL;
         $statement->expects($this->once())
             ->method('fetchAll')
             ->will($this->returnValue(['1']));
+        $statement->method('execute')->will($this->returnValue(true));
 
         $table = new TableSchema('articles');
         $result = $table->truncateSql($connection);
@@ -1066,6 +1067,7 @@ SQL;
         $statement->expects($this->once())
             ->method('fetchAll')
             ->will($this->returnValue(false));
+        $statement->method('execute')->will($this->returnValue(true));
 
         $table = new TableSchema('articles');
         $result = $table->truncateSql($connection);

+ 4 - 1
tests/TestCase/Database/Statement/StatementDecoratorTest.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -73,7 +74,9 @@ class StatementDecoratorTest extends TestCase
         $driver = $this->getMockBuilder('Cake\Database\Driver')->getMock();
         $statement = new StatementDecorator($inner, $driver);
 
-        $inner->expects($this->once())->method('execute');
+        $inner->expects($this->once())
+            ->method('execute')
+            ->will($this->returnValue(true));
         $this->assertSame($inner, $statement->getIterator());
         $this->assertSame($inner, $statement->getIterator());
     }