diff --git a/flight/Engine.php b/flight/Engine.php index bb694cb0..effb0af9 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -63,6 +63,8 @@ * # HTTP caching * @method void etag(string $id, ('strong'|'weak') $type = 'strong') Handles ETag HTTP caching. * @method void lastModified(int $time) Handles last modified HTTP caching. + * + * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore */ class Engine { diff --git a/flight/database/PdoWrapper.php b/flight/database/PdoWrapper.php index 842038c7..bdbabb89 100644 --- a/flight/database/PdoWrapper.php +++ b/flight/database/PdoWrapper.php @@ -4,6 +4,7 @@ namespace flight\database; +use flight\util\Collection; use PDO; use PDOStatement; @@ -47,8 +48,8 @@ public function runQuery(string $sql, array $params = []): PDOStatement */ public function fetchField(string $sql, array $params = []) { - $data = $this->fetchRow($sql, $params); - return reset($data); + $result = $this->fetchRow($sql, $params); + return reset($result); } /** @@ -59,13 +60,13 @@ public function fetchField(string $sql, array $params = []) * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * @param array $params - Ex: [ $something ] * - * @return array + * @return Collection */ - public function fetchRow(string $sql, array $params = []): array + public function fetchRow(string $sql, array $params = []): Collection { $sql .= stripos($sql, 'LIMIT') === false ? ' LIMIT 1' : ''; $result = $this->fetchAll($sql, $params); - return count($result) > 0 ? $result[0] : []; + return count($result) > 0 ? $result[0] : new Collection(); } /** @@ -79,17 +80,24 @@ public function fetchRow(string $sql, array $params = []): array * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * @param array $params - Ex: [ $something ] * - * @return array> + * @return array */ - public function fetchAll(string $sql, array $params = []): array + public function fetchAll(string $sql, array $params = []) { $processed_sql_data = $this->processInStatementSql($sql, $params); $sql = $processed_sql_data['sql']; $params = $processed_sql_data['params']; $statement = $this->prepare($sql); $statement->execute($params); - $result = $statement->fetchAll(); - return is_array($result) ? $result : []; + $results = $statement->fetchAll(); + if (is_array($results) === true && count($results) > 0) { + foreach ($results as &$result) { + $result = new Collection($result); + } + } else { + $results = []; + } + return $results; } /** diff --git a/flight/util/Collection.php b/flight/util/Collection.php index 6ffe0b5c..29147d16 100644 --- a/flight/util/Collection.php +++ b/flight/util/Collection.php @@ -20,6 +20,18 @@ */ class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable { + /** + * This is to allow for reset() to work properly. + * + * WARNING! This MUST be the first variable in this class!!! + * + * PHPStan is ignoring this because we don't need actually to read the property + * + * @var mixed + * @phpstan-ignore-next-line + */ + private $first_property = null; + /** * Collection data. * @@ -35,6 +47,7 @@ class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable public function __construct(array $data = []) { $this->data = $data; + $this->handleReset(); } /** @@ -55,6 +68,7 @@ public function __get(string $key) public function __set(string $key, $value): void { $this->data[$key] = $value; + $this->handleReset(); } /** @@ -71,6 +85,7 @@ public function __isset(string $key): bool public function __unset(string $key): void { unset($this->data[$key]); + $this->handleReset(); } /** @@ -100,6 +115,7 @@ public function offsetSet($offset, $value): void } else { $this->data[$offset] = $value; } + $this->handleReset(); } /** @@ -120,6 +136,17 @@ public function offsetExists($offset): bool public function offsetUnset($offset): void { unset($this->data[$offset]); + $this->handleReset(); + } + + /** + * This is to allow for reset() of a Collection to work properly. + * + * @return void + */ + public function handleReset() + { + $this->first_property = reset($this->data); } /** @@ -207,6 +234,7 @@ public function getData(): array public function setData(array $data): void { $this->data = $data; + $this->handleReset(); } #[\ReturnTypeWillChange] diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index b6eac466..4f742feb 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -99,4 +99,27 @@ public function testClear() $this->collection->clear(); $this->assertEquals(0, $this->collection->count()); } + + public function testResetByProperty() + { + $this->collection->a = 11; + $this->collection->b = 22; + $result = reset($this->collection); + $this->assertEquals(11, $result); + } + + public function testResetBySetData() + { + $this->collection->setData(['a' => 11, 'b' => 22]); + $result = reset($this->collection); + $this->assertEquals(11, $result); + } + + public function testResetByArraySet() + { + $this->collection['a'] = 11; + $this->collection['b'] = 22; + $result = reset($this->collection); + $this->assertEquals(11, $result); + } } diff --git a/tests/PdoWrapperTest.php b/tests/PdoWrapperTest.php index 324c47bc..0f41a92a 100644 --- a/tests/PdoWrapperTest.php +++ b/tests/PdoWrapperTest.php @@ -88,6 +88,13 @@ public function testFetchAll() $this->assertEquals('three', $rows[2]['name']); } + public function testFetchAllNoRows() + { + $rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE 1 = 2'); + $this->assertCount(0, $rows); + $this->assertSame([], $rows); + } + public function testFetchAllWithNamedParams() { $rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE name = :name', [ 'name' => 'two']);