From 7358bbdb328bf5a3bfe679f65e897aaea098871c Mon Sep 17 00:00:00 2001 From: connor Date: Thu, 10 Oct 2024 20:03:17 -0400 Subject: [PATCH 01/11] Level 1 --- composer.json | 3 ++- phpstan.neon | 5 +++++ src/PdoStatement.php | 24 ++++++++++++------------ 3 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 phpstan.neon diff --git a/composer.json b/composer.json index 88a744c..4c0040f 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ }, "require-dev": { "phpunit/phpunit": "^9.6|^10.5|^11.3", - "phpunit/php-code-coverage": "^9.2|^10.1|^11.0" + "phpunit/php-code-coverage": "^9.2|^10.1|^11.0", + "phpstan/phpstan": "^1.12" }, "autoload": { "psr-4": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..d1e2e58 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 1 + paths: + - src + phpVersion: 80200 diff --git a/src/PdoStatement.php b/src/PdoStatement.php index dd787d6..cf01c9f 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -16,7 +16,7 @@ class PdoStatement extends \PDOStatement * @var Result; */ private Result $result; - private int $fetchMode = PDO::FETCH_BOTH; //DEFAULT FETCHMODE + private int $fetchMode = Pdo::FETCH_BOTH; //DEFAULT FETCHMODE private array $boundParams = []; private array $boundColumns = []; @@ -77,7 +77,7 @@ public function execute(array $params = null) : bool return $success; } - public function fetch($mode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) : mixed + public function fetch($mode = null, $cursorOrientation = Pdo::FETCH_ORI_NEXT, $cursorOffset = 0) : mixed { // scrolling cursors not implemented $row = $this->result->nextRow(); @@ -91,7 +91,7 @@ public function fetch($mode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $c public function bindParam( $param, &$var, - $type = PDO::PARAM_STR, + $type = Pdo::PARAM_STR, $maxLength = null, $driverOptions = null ) : bool { @@ -107,7 +107,7 @@ public function bindColumn($column, &$var, $type = null, $maxLength = null, $dri return true; } - public function bindValue($param, $value, $type = PDO::PARAM_STR) : bool + public function bindValue($param, $value, $type = Pdo::PARAM_STR) : bool { $this->boundParams[$param] = $value; @@ -123,7 +123,7 @@ public function fetchColumn($column = 0) : mixed { $row = $this->result->nextRow(); if ($row) { - $row = $this->proccessFetchedRow($row, PDO::FETCH_NUM); + $row = $this->proccessFetchedRow($row, Pdo::FETCH_NUM); return $row[$column]; } @@ -131,7 +131,7 @@ public function fetchColumn($column = 0) : mixed return false; } - public function fetchAll(int $mode = PDO::FETCH_DEFAULT, mixed ...$args) : array + public function fetchAll(int $mode = Pdo::FETCH_DEFAULT, mixed ...$args) : array { $rows = $this->result->getRows() ?? []; $returnArray = []; @@ -146,7 +146,7 @@ private function proccessFetchedRow($row, $fetchMode) : mixed { $i = 0; switch ($fetchMode ?: $this->fetchMode) { - case PDO::FETCH_BOTH: + case Pdo::FETCH_BOTH: $returnRow = []; $keys = array_keys($row); $c = 0; @@ -156,13 +156,13 @@ private function proccessFetchedRow($row, $fetchMode) : mixed } return $returnRow; - case PDO::FETCH_ASSOC: + case Pdo::FETCH_ASSOC: return $row; - case PDO::FETCH_NUM: + case Pdo::FETCH_NUM: return array_values($row); - case PDO::FETCH_OBJ: + case Pdo::FETCH_OBJ: return (object)$row; - case PDO::FETCH_BOUND: + case Pdo::FETCH_BOUND: if ($this->result->isOrdinalArray($this->boundColumns)) { foreach ($this->boundColumns as &$column) { $column = array_values($row)[++$i]; @@ -174,7 +174,7 @@ private function proccessFetchedRow($row, $fetchMode) : mixed } return true; - case PDO::FETCH_COLUMN: + case Pdo::FETCH_COLUMN: $returnRow = array_values($row); return $returnRow[0]; From 658a076f34a7a367df2736d9cfe34e23f83e4509 Mon Sep 17 00:00:00 2001 From: connor Date: Thu, 10 Oct 2024 20:04:44 -0400 Subject: [PATCH 02/11] Level 2 --- phpstan.neon | 2 +- src/PdoStatement.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index d1e2e58..abb7667 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 1 + level: 2 paths: - src phpVersion: 80200 diff --git a/src/PdoStatement.php b/src/PdoStatement.php index cf01c9f..58e2534 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -187,7 +187,7 @@ private function proccessFetchedRow($row, $fetchMode) : mixed * @param string $class * @param null $constructorArgs * - * @return bool|mixed + * @return object|false * @throws ReflectionException */ public function fetchObject($class = stdClass::class, $constructorArgs = null) : object|false From c625a44d5ce077cd35bbf3917f88bd69f45129c0 Mon Sep 17 00:00:00 2001 From: connor Date: Thu, 10 Oct 2024 20:25:15 -0400 Subject: [PATCH 03/11] Level 3 --- phpstan.neon | 2 +- src/Pdo.php | 20 +++++++------------- src/PdoStatement.php | 14 +++++++------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index abb7667..35f59ea 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 2 + level: 3 paths: - src phpVersion: 80200 diff --git a/src/Pdo.php b/src/Pdo.php index c016927..e8471d1 100644 --- a/src/Pdo.php +++ b/src/Pdo.php @@ -103,18 +103,12 @@ public function query(string $query, ?int $fetchMode = null, mixed ...$fetchMode /** * @param null $name * - * @return false|string - * @throws PseudoException + * @return string|false + * @throws PseudoException|Throwable */ - public function lastInsertId($name = null) : false|string + public function lastInsertId($name = null) : string|false { - $result = $this->getLastResult(); - - if (!$result) { - return false; - } - - return $result->getInsertId(); + return $this->getLastResult() !== false ? $this->getLastResult()->getInsertId() : false; } /** @@ -149,9 +143,9 @@ public function load($filePath) : void } /** - * @param string $sql - * @param array|null $params - * @param mixed $expectedResults + * @param string $sql + * @param array|null $params + * @param mixed $expectedResults */ public function mock(string $sql, ?array $params = null, mixed $expectedResults = null) : void { diff --git a/src/PdoStatement.php b/src/PdoStatement.php index 58e2534..38254e1 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -82,7 +82,7 @@ public function fetch($mode = null, $cursorOrientation = Pdo::FETCH_ORI_NEXT, $c // scrolling cursors not implemented $row = $this->result->nextRow(); if ($row) { - return $this->proccessFetchedRow($row, $mode); + return $this->processFetchedRow($row, $mode); } return false; @@ -123,7 +123,7 @@ public function fetchColumn($column = 0) : mixed { $row = $this->result->nextRow(); if ($row) { - $row = $this->proccessFetchedRow($row, Pdo::FETCH_NUM); + $row = $this->processFetchedRow($row, Pdo::FETCH_NUM); return $row[$column]; } @@ -136,13 +136,13 @@ public function fetchAll(int $mode = Pdo::FETCH_DEFAULT, mixed ...$args) : array $rows = $this->result->getRows() ?? []; $returnArray = []; foreach ($rows as $row) { - $returnArray[] = $this->proccessFetchedRow($row, $mode); + $returnArray[] = $this->processFetchedRow($row, $mode); } return $returnArray; } - private function proccessFetchedRow($row, $fetchMode) : mixed + private function processFetchedRow($row, $fetchMode) : mixed { $i = 0; switch ($fetchMode ?: $this->fetchMode) { @@ -185,17 +185,17 @@ private function proccessFetchedRow($row, $fetchMode) : mixed /** * @param string $class - * @param null $constructorArgs + * @param array $constructorArgs * * @return object|false * @throws ReflectionException */ - public function fetchObject($class = stdClass::class, $constructorArgs = null) : object|false + public function fetchObject($class = stdClass::class, array $constructorArgs = []) : object|false { $row = $this->result->nextRow(); if ($row) { $reflect = new ReflectionClass($class); - $obj = $reflect->newInstanceArgs($constructorArgs ?: []); + $obj = $reflect->newInstanceArgs($constructorArgs); foreach ($row as $key => $val) { $obj->$key = $val; } From 7d2bb1bf06f6f41bfca3a7c41ae23180e5b47cdb Mon Sep 17 00:00:00 2001 From: connor Date: Thu, 10 Oct 2024 20:27:29 -0400 Subject: [PATCH 04/11] Level 4 --- phpstan.neon | 2 +- src/PdoStatement.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 35f59ea..eaaa80c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 3 + level: 4 paths: - src phpVersion: 80200 diff --git a/src/PdoStatement.php b/src/PdoStatement.php index 38254e1..406e3f7 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -28,11 +28,11 @@ class PdoStatement extends \PDOStatement private ?string $statement; /** - * @param null $result + * @param mixed $result * @param QueryLog|null $queryLog * @param null $statement */ - public function __construct($result = null, QueryLog $queryLog = null, $statement = null) + public function __construct(mixed $result = null, QueryLog $queryLog = null, $statement = null) { if (!($result instanceof Result)) { $result = new Result(); From 132f3c2eb8ca1bbfab3bb9cb031b9fd497ab7d96 Mon Sep 17 00:00:00 2001 From: connor Date: Thu, 10 Oct 2024 20:28:50 -0400 Subject: [PATCH 05/11] Level 4 --- phpstan.neon | 2 +- src/PdoStatement.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index eaaa80c..4bf8366 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 4 + level: 5 paths: - src phpVersion: 80200 diff --git a/src/PdoStatement.php b/src/PdoStatement.php index 406e3f7..bb41f11 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -30,7 +30,7 @@ class PdoStatement extends \PDOStatement /** * @param mixed $result * @param QueryLog|null $queryLog - * @param null $statement + * @param string|null $statement */ public function __construct(mixed $result = null, QueryLog $queryLog = null, $statement = null) { From fd959a325e075354d22b7ac037d3a664df6c1d06 Mon Sep 17 00:00:00 2001 From: Connor Smyth Date: Fri, 11 Oct 2024 22:38:26 -0400 Subject: [PATCH 06/11] Level 6 --- phpstan.neon | 2 +- src/ParsedQuery.php | 3 +- src/Pdo.php | 54 +++++++++-------- src/PdoStatement.php | 122 ++++++++++++++++++++++++--------------- src/QueryLog.php | 14 ++++- src/Result.php | 67 ++++++++++++++++----- src/ResultCollection.php | 11 +++- 7 files changed, 181 insertions(+), 92 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 4bf8366..6263e75 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 5 + level: 6 paths: - src phpVersion: 80200 diff --git a/src/ParsedQuery.php b/src/ParsedQuery.php index e8e5e78..9661485 100644 --- a/src/ParsedQuery.php +++ b/src/ParsedQuery.php @@ -1,4 +1,5 @@ hash = sha1($query); } - public function isEqualTo($query) + public function isEqualTo(ParsedQuery|string $query): bool { if (!($query instanceof self)) { $query = new self($query); diff --git a/src/Pdo.php b/src/Pdo.php index e8471d1..d18a449 100644 --- a/src/Pdo.php +++ b/src/Pdo.php @@ -13,26 +13,30 @@ class Pdo extends \PDO private QueryLog $queryLog; /** - * @param ResultCollection|null $collection + * @param ResultCollection|null $collection */ public function __construct( ResultCollection $collection = null ) { $this->mockedQueries = $collection ?? new ResultCollection(); - $this->queryLog = new QueryLog(); + $this->queryLog = new QueryLog(); } /** - * @throws PseudoException|Throwable + * @param ParsedQuery|string $query + * @param array $options + * @return PdoStatement + * @throws PseudoException + * @throws Throwable */ - public function prepare($query, $options = null) : PdoStatement + public function prepare(ParsedQuery|string $query, array $options = []): PdoStatement { $result = $this->mockedQueries->getResult($query); return new PdoStatement($result, $this->queryLog, $query); } - public function beginTransaction() : bool + public function beginTransaction(): bool { if (!$this->inTransaction) { $this->inTransaction = true; @@ -43,7 +47,7 @@ public function beginTransaction() : bool return false; } - public function commit() : bool + public function commit(): bool { if ($this->inTransaction()) { $this->inTransaction = false; @@ -54,7 +58,7 @@ public function commit() : bool return false; } - public function rollBack() : bool + public function rollBack(): bool { if ($this->inTransaction()) { $this->inTransaction = false; @@ -65,12 +69,12 @@ public function rollBack() : bool return false; } - public function inTransaction() : bool + public function inTransaction(): bool { return $this->inTransaction; } - public function exec($statement) : false|int + public function exec($statement): false|int { $result = $this->query($statement); @@ -78,14 +82,14 @@ public function exec($statement) : false|int } /** - * @param string $query - * @param int|null $fetchMode - * @param mixed ...$fetchModeArgs + * @param string $query + * @param int|null $fetchMode + * @param mixed ...$fetchModeArgs * * @return PdoStatement * @throws PseudoException|Throwable */ - public function query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs) : PdoStatement + public function query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs): PdoStatement { if ($this->mockedQueries->exists($query)) { $result = $this->mockedQueries->getResult($query); @@ -101,12 +105,12 @@ public function query(string $query, ?int $fetchMode = null, mixed ...$fetchMode } /** - * @param null $name + * @param null $name * * @return string|false * @throws PseudoException|Throwable */ - public function lastInsertId($name = null) : string|false + public function lastInsertId($name = null): string|false { return $this->getLastResult() !== false ? $this->getLastResult()->getInsertId() : false; } @@ -115,7 +119,7 @@ public function lastInsertId($name = null) : string|false * @return Result|false * @throws PseudoException|Throwable */ - private function getLastResult() : Result|false + private function getLastResult(): Result|false { try { $lastQuery = $this->queryLog[count($this->queryLog) - 1]; @@ -127,27 +131,27 @@ private function getLastResult() : Result|false } /** - * @param string $filePath + * @param string $filePath */ - public function save(string $filePath) : void + public function save(string $filePath): void { file_put_contents($filePath, serialize($this->mockedQueries)); } /** - * @param $filePath + * @param string $filePath */ - public function load($filePath) : void + public function load(string $filePath): void { $this->mockedQueries = unserialize(file_get_contents($filePath)); } /** - * @param string $sql - * @param array|null $params - * @param mixed $expectedResults + * @param string $sql + * @param array|null $params + * @param mixed $expectedResults */ - public function mock(string $sql, ?array $params = null, mixed $expectedResults = null) : void + public function mock(string $sql, ?array $params = null, mixed $expectedResults = null): void { $this->mockedQueries->addQuery($sql, $params, $expectedResults); } @@ -155,7 +159,7 @@ public function mock(string $sql, ?array $params = null, mixed $expectedResults /** * @return ResultCollection */ - public function getMockedQueries() : ResultCollection + public function getMockedQueries(): ResultCollection { return $this->mockedQueries; } diff --git a/src/PdoStatement.php b/src/PdoStatement.php index bb41f11..3df7799 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -4,6 +4,7 @@ use ArrayIterator; use Iterator; +use Pseudo\Exceptions\LogicException; use Pseudo\Exceptions\PseudoException; use ReflectionClass; use ReflectionException; @@ -16,8 +17,14 @@ class PdoStatement extends \PDOStatement * @var Result; */ private Result $result; - private int $fetchMode = Pdo::FETCH_BOTH; //DEFAULT FETCHMODE + private int $fetchMode = \PDO::FETCH_BOTH; //DEFAULT FETCHMODE + /** + * @var array + */ private array $boundParams = []; + /** + * @var array + */ private array $boundColumns = []; /** @@ -28,9 +35,9 @@ class PdoStatement extends \PDOStatement private ?string $statement; /** - * @param mixed $result - * @param QueryLog|null $queryLog - * @param string|null $statement + * @param mixed $result + * @param QueryLog|null $queryLog + * @param string|null $statement */ public function __construct(mixed $result = null, QueryLog $queryLog = null, $statement = null) { @@ -41,23 +48,24 @@ public function __construct(mixed $result = null, QueryLog $queryLog = null, $st if (!($queryLog instanceof QueryLog)) { $queryLog = new QueryLog(); } - $this->queryLog = $queryLog; + $this->queryLog = $queryLog; $this->statement = $statement; } - public function setResult(Result $result) : void + public function setResult(Result $result): void { $this->result = $result; } /** - * @param array|null $params + * @param array|null $params * * @return bool + * @throws LogicException */ - public function execute(array $params = null) : bool + public function execute(array $params = null): bool { - $params = array_merge((array)$params, $this->boundParams); + $params = array_merge((array) $params, $this->boundParams); $this->result->setParams($params, !empty($this->boundParams)); $this->queryLog->addQuery($this->statement); @@ -68,7 +76,7 @@ public function execute(array $params = null) : bool // TODO: I'm not a fan of exception-based logic, but I want to avoid a backwards incompatibility change here. // Perhaps we can address in the next major version update try { - $success = (bool)$this->result->getRows($params ?: []); + $success = (bool) $this->result->getRows($params ?: []); } catch (PseudoException) { $success = false; } @@ -77,7 +85,7 @@ public function execute(array $params = null) : bool return $success; } - public function fetch($mode = null, $cursorOrientation = Pdo::FETCH_ORI_NEXT, $cursorOffset = 0) : mixed + public function fetch($mode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0): mixed { // scrolling cursors not implemented $row = $this->result->nextRow(); @@ -91,39 +99,39 @@ public function fetch($mode = null, $cursorOrientation = Pdo::FETCH_ORI_NEXT, $c public function bindParam( $param, &$var, - $type = Pdo::PARAM_STR, + $type = \PDO::PARAM_STR, $maxLength = null, $driverOptions = null - ) : bool { + ): bool { $this->boundParams[$param] =& $var; return true; } - public function bindColumn($column, &$var, $type = null, $maxLength = null, $driverOptions = null) : bool + public function bindColumn($column, &$var, $type = null, $maxLength = null, $driverOptions = null): bool { $this->boundColumns[$column] =& $var; return true; } - public function bindValue($param, $value, $type = Pdo::PARAM_STR) : bool + public function bindValue($param, $value, $type = \PDO::PARAM_STR): bool { $this->boundParams[$param] = $value; return true; } - public function rowCount() : int + public function rowCount(): int { return $this->result->getAffectedRowCount(); } - public function fetchColumn($column = 0) : mixed + public function fetchColumn($column = 0): mixed { $row = $this->result->nextRow(); if ($row) { - $row = $this->processFetchedRow($row, Pdo::FETCH_NUM); + $row = $this->processFetchedRow($row, \PDO::FETCH_NUM); return $row[$column]; } @@ -131,9 +139,15 @@ public function fetchColumn($column = 0) : mixed return false; } - public function fetchAll(int $mode = Pdo::FETCH_DEFAULT, mixed ...$args) : array + /** + * @param int $mode + * @param mixed ...$args + * @return array + * @throws PseudoException + */ + public function fetchAll(int $mode = \PDO::FETCH_DEFAULT, mixed ...$args): array { - $rows = $this->result->getRows() ?? []; + $rows = $this->result->getRows() ?? []; $returnArray = []; foreach ($rows as $row) { $returnArray[] = $this->processFetchedRow($row, $mode); @@ -142,27 +156,32 @@ public function fetchAll(int $mode = Pdo::FETCH_DEFAULT, mixed ...$args) : array return $returnArray; } - private function processFetchedRow($row, $fetchMode) : mixed + /** + * @param array $row + * @param int|null $fetchMode + * @return mixed + */ + private function processFetchedRow(array $row, ?int $fetchMode): mixed { $i = 0; switch ($fetchMode ?: $this->fetchMode) { - case Pdo::FETCH_BOTH: + case \PDO::FETCH_BOTH: $returnRow = []; - $keys = array_keys($row); - $c = 0; + $keys = array_keys($row); + $c = 0; foreach ($keys as $key) { $returnRow[$key] = $row[$key]; $returnRow[$c++] = $row[$key]; } return $returnRow; - case Pdo::FETCH_ASSOC: + case \PDO::FETCH_ASSOC: return $row; - case Pdo::FETCH_NUM: + case \PDO::FETCH_NUM: return array_values($row); - case Pdo::FETCH_OBJ: - return (object)$row; - case Pdo::FETCH_BOUND: + case \PDO::FETCH_OBJ: + return (object) $row; + case \PDO::FETCH_BOUND: if ($this->result->isOrdinalArray($this->boundColumns)) { foreach ($this->boundColumns as &$column) { $column = array_values($row)[++$i]; @@ -174,7 +193,7 @@ private function processFetchedRow($row, $fetchMode) : mixed } return true; - case Pdo::FETCH_COLUMN: + case \PDO::FETCH_COLUMN: $returnRow = array_values($row); return $returnRow[0]; @@ -184,18 +203,18 @@ private function processFetchedRow($row, $fetchMode) : mixed } /** - * @param string $class - * @param array $constructorArgs + * @param string $class + * @param array $constructorArgs * * @return object|false * @throws ReflectionException */ - public function fetchObject($class = stdClass::class, array $constructorArgs = []) : object|false + public function fetchObject($class = stdClass::class, array $constructorArgs = []): object|false { $row = $this->result->nextRow(); if ($row) { $reflect = new ReflectionClass($class); - $obj = $reflect->newInstanceArgs($constructorArgs); + $obj = $reflect->newInstanceArgs($constructorArgs); foreach ($row as $key => $val) { $obj->$key = $val; } @@ -209,15 +228,15 @@ public function fetchObject($class = stdClass::class, array $constructorArgs = [ /** * @return string|null */ - public function errorCode() : ?string + public function errorCode(): ?string { return $this->result->getErrorCode(); } /** - * @return array + * @return array */ - public function errorInfo() : array + public function errorInfo(): array { return [$this->result->getErrorInfo()]; } @@ -226,7 +245,7 @@ public function errorInfo() : array * @return int * @throws PseudoException */ - public function columnCount() : int + public function columnCount(): int { $rows = $this->result->getRows(); if ($rows) { @@ -239,21 +258,21 @@ public function columnCount() : int } /** - * @param int $mode - * @param null $className - * @param mixed ...$params + * @param int $mode + * @param null $className + * @param mixed ...$params * * @return bool|int */ - public function setFetchMode($mode, $className = null, ...$params) : bool|int + public function setFetchMode($mode, $className = null, ...$params): bool|int { - $r = new ReflectionClass(new Pdo()); - $constants = $r->getConstants(); - $constantNames = array_keys($constants); + $r = new ReflectionClass(new Pdo()); + $constants = $r->getConstants(); + $constantNames = array_keys($constants); $allowedConstantNames = array_filter($constantNames, function ($val) { return str_starts_with($val, 'FETCH_'); }); - $allowedConstantVals = []; + $allowedConstantVals = []; foreach ($allowedConstantNames as $name) { $allowedConstantVals[] = $constants[$name]; } @@ -267,12 +286,19 @@ public function setFetchMode($mode, $className = null, ...$params) : bool|int return false; } - public function getBoundParams() : array + /** + * @return array + */ + public function getBoundParams(): array { return $this->boundParams; } - public function getIterator() : Iterator + /** + * @return Iterator + * @throws PseudoException + */ + public function getIterator(): Iterator { return new ArrayIterator($this->fetchAll()); } diff --git a/src/QueryLog.php b/src/QueryLog.php index a59bcf5..b738da2 100644 --- a/src/QueryLog.php +++ b/src/QueryLog.php @@ -9,9 +9,16 @@ use IteratorAggregate; use Traversable; +/** + * @implements IteratorAggregate + * @implements ArrayAccess + */ class QueryLog implements IteratorAggregate, ArrayAccess, Countable { - private $queries = []; + /** + * @var array + */ + private array $queries = []; public function count(): int { @@ -47,11 +54,14 @@ public function offsetUnset($offset): void unset($this->queries[$offset]); } - public function addQuery($sql): void + public function addQuery(string $sql): void { $this->queries[] = new ParsedQuery($sql); } + /** + * @return array + */ public function getQueries(): array { return $this->queries; diff --git a/src/Result.php b/src/Result.php index 9725828..e616669 100644 --- a/src/Result.php +++ b/src/Result.php @@ -7,17 +7,28 @@ class Result { + /** + * @var array + */ private array $rows = []; - private ?bool $executionResult = null; + private ?bool $executionResult; private bool $isParameterized = false; private string $errorCode; private string $errorInfo; private int $affectedRowCount = 0; private int $insertId = 0; private int $rowOffset = 0; + /** + * @var array + */ private array $params = []; - public function __construct($rows = null, $params = null, $executionResult = null) + /** + * @param array|null $rows + * @param array|null $params + * @param bool|null $executionResult + */ + public function __construct(?array $rows = null, ?array $params = null, ?bool $executionResult = null) { if (is_array($rows)) { if ($params) { @@ -32,9 +43,12 @@ public function __construct($rows = null, $params = null, $executionResult = nul } /** + * @param array $row + * @param array $params + * @return void * @throws PseudoException */ - public function addRow(array $row, $params = null): void + public function addRow(array $row, ?array $params = null): void { if (empty($row)) { return; @@ -53,7 +67,12 @@ public function addRow(array $row, $params = null): void } } - public function setParams($params, bool $parameterize = false): void + /** + * @param array $params + * @param bool $parameterize + * @return void + */ + public function setParams(array $params, bool $parameterize = false): void { $this->params = $params; if ($parameterize) { @@ -61,7 +80,12 @@ public function setParams($params, bool $parameterize = false): void } } - public function getRows(array $params = []) + /** + * @param array $params + * @return mixed + * @throws PseudoException + */ + public function getRows(array $params = []): mixed { if (!empty($this->params) && empty($params)) { $params = $this->params; @@ -88,9 +112,9 @@ public function getRows(array $params = []) /** * Returns the next row if it exists, otherwise returns false * - * @param array $rows Rows to get row from + * @param array $rows Rows to get row from * - * @return false|array Next row (false if doesn't exist) + * @return false|array Next row (false if it doesn't exist) */ private function getRowIfExists(array $rows): false|array { @@ -104,7 +128,7 @@ private function getRowIfExists(array $rows): false|array /** * Returns the next available row if it exists, otherwise returns false * - * @return false|array + * @return false|array */ public function nextRow(): false|array { @@ -126,7 +150,7 @@ public function nextRow(): false|array } - public function setInsertId($insertId): void + public function setInsertId(int $insertId): void { $this->insertId = $insertId; } @@ -137,11 +161,11 @@ public function getInsertId(): int } /** - * @param $errorCode + * @param string $errorCode * * @throws PseudoException */ - public function setErrorCode($errorCode): void + public function setErrorCode(string $errorCode): void { if (ctype_alnum($errorCode) && strlen($errorCode) == 5) { $this->errorCode = $errorCode; @@ -159,9 +183,9 @@ public function getErrorCode(): string } /** - * @param $errorInfo + * @param string $errorInfo */ - public function setErrorInfo($errorInfo): void + public function setErrorInfo(string $errorInfo): void { $this->errorInfo = $errorInfo; } @@ -174,7 +198,7 @@ public function getErrorInfo(): string return $this->errorInfo; } - public function setAffectedRowCount($affectedRowCount): void + public function setAffectedRowCount(int $affectedRowCount): void { $this->affectedRowCount = $affectedRowCount; } @@ -184,6 +208,10 @@ public function getAffectedRowCount(): int return $this->affectedRowCount; } + /** + * @param array $arr + * @return bool + */ public function isOrdinalArray(array $arr): bool { return !(is_string(key($arr))); @@ -215,6 +243,10 @@ public function getExecutionResult(): bool return $this->executionResult; } + /** + * @param array $params + * @return string + */ private function stringifyParameterSet(array $params): string { if ($this->isOrdinalArray($params)) { @@ -230,6 +262,11 @@ private function stringifyParameterSet(array $params): string } } + /** + * @param string $parameterKey + * @param array $row + * @return void + */ private function initializeParameterizedRows(string $parameterKey, array $row): void { if (empty($this->rows)) { @@ -239,6 +276,8 @@ private function initializeParameterizedRows(string $parameterKey, array $row): } /** + * @param array $row + * @return void * @throws PseudoException */ private function addNonParameterizedRow(array $row): void diff --git a/src/ResultCollection.php b/src/ResultCollection.php index 40675d6..f2bf534 100644 --- a/src/ResultCollection.php +++ b/src/ResultCollection.php @@ -8,6 +8,9 @@ class ResultCollection implements Countable { + /** + * @var array + */ private array $queries = []; public function count(): int @@ -15,6 +18,12 @@ public function count(): int return count($this->queries); } + /** + * @param string $sql + * @param array|null $params + * @param mixed|null $results + * @return void + */ public function addQuery(string $sql, ?array $params = null, mixed $results = null): void { $query = new ParsedQuery($sql); @@ -34,7 +43,7 @@ public function addQuery(string $sql, ?array $params = null, mixed $results = nu $this->queries[$query->getHash()] = $storedResults; } - public function exists($sql): bool + public function exists(string $sql): bool { $query = new ParsedQuery($sql); return isset($this->queries[$query->getHash()]); From 9ab2816d935561d43c4a86d7123ee521491445f1 Mon Sep 17 00:00:00 2001 From: Connor Smyth Date: Fri, 11 Oct 2024 22:50:06 -0400 Subject: [PATCH 07/11] Level 7 --- phpstan.neon | 2 +- src/Pdo.php | 32 +++++++++++++++++++++++--------- src/PdoStatement.php | 18 +++++++++++------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 6263e75..6ff9138 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 6 + level: 7 paths: - src phpVersion: 80200 diff --git a/src/Pdo.php b/src/Pdo.php index d18a449..856cb12 100644 --- a/src/Pdo.php +++ b/src/Pdo.php @@ -23,13 +23,13 @@ public function __construct( } /** - * @param ParsedQuery|string $query + * @param string $query * @param array $options * @return PdoStatement * @throws PseudoException * @throws Throwable */ - public function prepare(ParsedQuery|string $query, array $options = []): PdoStatement + public function prepare(string $query, array $options = []): PdoStatement { $result = $this->mockedQueries->getResult($query); @@ -105,21 +105,28 @@ public function query(string $query, ?int $fetchMode = null, mixed ...$fetchMode } /** - * @param null $name + * @param string|null $name * * @return string|false - * @throws PseudoException|Throwable + * @throws PseudoException + * @throws Throwable */ - public function lastInsertId($name = null): string|false + public function lastInsertId(?string $name = null): string|false { - return $this->getLastResult() !== false ? $this->getLastResult()->getInsertId() : false; + $lastResult = $this->getLastResult(); + + if (is_bool($lastResult)) { + return false; + } + + return (string) $lastResult->getInsertId(); } /** - * @return Result|false + * @return Result|bool * @throws PseudoException|Throwable */ - private function getLastResult(): Result|false + private function getLastResult(): Result|bool { try { $lastQuery = $this->queryLog[count($this->queryLog) - 1]; @@ -140,10 +147,17 @@ public function save(string $filePath): void /** * @param string $filePath + * @throws PseudoException */ public function load(string $filePath): void { - $this->mockedQueries = unserialize(file_get_contents($filePath)); + $fileContents = file_get_contents($filePath); + + if ($fileContents === false) { + throw new PseudoException('Unable to read file: ' . $filePath); + } + + $this->mockedQueries = unserialize($fileContents); } /** diff --git a/src/PdoStatement.php b/src/PdoStatement.php index 3df7799..ab72510 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -12,10 +12,6 @@ class PdoStatement extends \PDOStatement { - - /** - * @var Result; - */ private Result $result; private int $fetchMode = \PDO::FETCH_BOTH; //DEFAULT FETCHMODE /** @@ -52,8 +48,12 @@ public function __construct(mixed $result = null, QueryLog $queryLog = null, $st $this->statement = $statement; } - public function setResult(Result $result): void + public function setResult(Result|bool $result): void { + if (is_bool($result)) { + return; + } + $this->result = $result; } @@ -203,14 +203,18 @@ private function processFetchedRow(array $row, ?int $fetchMode): mixed } /** - * @param string $class + * @param class-string|null $class * @param array $constructorArgs * * @return object|false * @throws ReflectionException */ - public function fetchObject($class = stdClass::class, array $constructorArgs = []): object|false + public function fetchObject(?string $class = "stdClass", array $constructorArgs = []): object|false { + if (is_null($class)) { + return false; + } + $row = $this->result->nextRow(); if ($row) { $reflect = new ReflectionClass($class); From dd6fac3e6b07308e6cc1e2d3519294716318564d Mon Sep 17 00:00:00 2001 From: Connor Smyth Date: Fri, 11 Oct 2024 22:53:20 -0400 Subject: [PATCH 08/11] Level 7=8 --- phpstan.neon | 2 +- src/PdoStatement.php | 6 +++--- src/QueryLog.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 6ff9138..f25f974 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 7 + level: 8 paths: - src phpVersion: 80200 diff --git a/src/PdoStatement.php b/src/PdoStatement.php index ab72510..4bf95be 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -28,14 +28,14 @@ class PdoStatement extends \PDOStatement */ private QueryLog $queryLog; - private ?string $statement; + private string $statement; /** * @param mixed $result * @param QueryLog|null $queryLog - * @param string|null $statement + * @param string $statement */ - public function __construct(mixed $result = null, QueryLog $queryLog = null, $statement = null) + public function __construct(mixed $result = null, QueryLog $queryLog = null, string $statement = '') { if (!($result instanceof Result)) { $result = new Result(); diff --git a/src/QueryLog.php b/src/QueryLog.php index b738da2..4146dc7 100644 --- a/src/QueryLog.php +++ b/src/QueryLog.php @@ -35,7 +35,7 @@ public function offsetExists($offset): bool return isset($this->queries[$offset]); } - public function offsetGet($offset): mixed + public function offsetGet(mixed $offset): ParsedQuery { if (!$this->offsetExists($offset)) { throw new InvalidArgumentException("Offset $offset does not exist"); From 288e61bc1ab7a7aac28a179974efff7b87d9d1a8 Mon Sep 17 00:00:00 2001 From: Connor Smyth Date: Fri, 11 Oct 2024 22:55:08 -0400 Subject: [PATCH 09/11] Adding workflow --- .github/workflows/test.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2be2a59..785bb63 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,9 +22,29 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - tools: phpunit - name: Run tests - phpunit run: | composer install ./vendor/bin/phpunit --configuration phpunit.xml.dist tests/Unit + + + static-analysis: + name: Static Analysis + runs-on: ubuntu-22.04 + strategy: + matrix: + php-versions: [ '8.0', '8.1', '8.2', '8.3' ] + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: Run tests - phpunit + run: | + composer install + ./vendor/bin/phpstan analyse --configuration phpstan.neon src From db62c52c309c05d29f2628de1b6b94e34be4807c Mon Sep 17 00:00:00 2001 From: Connor Smyth Date: Fri, 11 Oct 2024 23:04:11 -0400 Subject: [PATCH 10/11] WIP --- phpstan.neon | 2 +- src/Pdo.php | 5 ++++- src/PdoStatement.php | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index f25f974..91916fe 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 8 + level: 9 paths: - src phpVersion: 80200 diff --git a/src/Pdo.php b/src/Pdo.php index 856cb12..be4841e 100644 --- a/src/Pdo.php +++ b/src/Pdo.php @@ -157,7 +157,10 @@ public function load(string $filePath): void throw new PseudoException('Unable to read file: ' . $filePath); } - $this->mockedQueries = unserialize($fileContents); + /** @var ResultCollection $resultCollection */ + $resultCollection = unserialize($fileContents); + + $this->mockedQueries = $resultCollection; } /** diff --git a/src/PdoStatement.php b/src/PdoStatement.php index 4bf95be..c0271b9 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -130,10 +130,12 @@ public function rowCount(): int public function fetchColumn($column = 0): mixed { $row = $this->result->nextRow(); - if ($row) { + if (is_array($row)) { $row = $this->processFetchedRow($row, \PDO::FETCH_NUM); - return $row[$column]; + if (is_array($row)) { + return $row[$column]; + } } return false; From 6a5516b78cb48ebd2433da1e8e6e1e42a2e2d7e5 Mon Sep 17 00:00:00 2001 From: connor Date: Sat, 12 Oct 2024 09:41:55 -0400 Subject: [PATCH 11/11] Level 9 --- src/PdoStatement.php | 70 ++++++++++++++++++++++++-------------------- src/Result.php | 50 ++++++++++++++++++------------- 2 files changed, 69 insertions(+), 51 deletions(-) diff --git a/src/PdoStatement.php b/src/PdoStatement.php index c0271b9..c52fd99 100644 --- a/src/PdoStatement.php +++ b/src/PdoStatement.php @@ -8,7 +8,6 @@ use Pseudo\Exceptions\PseudoException; use ReflectionClass; use ReflectionException; -use stdClass; class PdoStatement extends \PDOStatement { @@ -31,9 +30,9 @@ class PdoStatement extends \PDOStatement private string $statement; /** - * @param mixed $result - * @param QueryLog|null $queryLog - * @param string $statement + * @param mixed $result + * @param QueryLog|null $queryLog + * @param string $statement */ public function __construct(mixed $result = null, QueryLog $queryLog = null, string $statement = '') { @@ -44,7 +43,7 @@ public function __construct(mixed $result = null, QueryLog $queryLog = null, str if (!($queryLog instanceof QueryLog)) { $queryLog = new QueryLog(); } - $this->queryLog = $queryLog; + $this->queryLog = $queryLog; $this->statement = $statement; } @@ -58,14 +57,14 @@ public function setResult(Result|bool $result): void } /** - * @param array|null $params + * @param array|null $params * * @return bool * @throws LogicException */ public function execute(array $params = null): bool { - $params = array_merge((array) $params, $this->boundParams); + $params = array_merge((array)$params, $this->boundParams); $this->result->setParams($params, !empty($this->boundParams)); $this->queryLog->addQuery($this->statement); @@ -76,7 +75,7 @@ public function execute(array $params = null): bool // TODO: I'm not a fan of exception-based logic, but I want to avoid a backwards incompatibility change here. // Perhaps we can address in the next major version update try { - $success = (bool) $this->result->getRows($params ?: []); + $success = (bool)$this->result->getRows($params ?: []); } catch (PseudoException) { $success = false; } @@ -142,25 +141,32 @@ public function fetchColumn($column = 0): mixed } /** - * @param int $mode - * @param mixed ...$args + * @param int $mode + * @param mixed ...$args + * * @return array * @throws PseudoException */ public function fetchAll(int $mode = \PDO::FETCH_DEFAULT, mixed ...$args): array { $rows = $this->result->getRows() ?? []; - $returnArray = []; - foreach ($rows as $row) { - $returnArray[] = $this->processFetchedRow($row, $mode); + + if (is_array($rows)) { + return array_map( + function ($row) use ($mode) { + return $this->processFetchedRow($row, $mode); + }, + $rows + ); } - return $returnArray; + return []; } /** - * @param array $row - * @param int|null $fetchMode + * @param array $row + * @param int|null $fetchMode + * * @return mixed */ private function processFetchedRow(array $row, ?int $fetchMode): mixed @@ -169,8 +175,8 @@ private function processFetchedRow(array $row, ?int $fetchMode): mixed switch ($fetchMode ?: $this->fetchMode) { case \PDO::FETCH_BOTH: $returnRow = []; - $keys = array_keys($row); - $c = 0; + $keys = array_keys($row); + $c = 0; foreach ($keys as $key) { $returnRow[$key] = $row[$key]; $returnRow[$c++] = $row[$key]; @@ -182,7 +188,7 @@ private function processFetchedRow(array $row, ?int $fetchMode): mixed case \PDO::FETCH_NUM: return array_values($row); case \PDO::FETCH_OBJ: - return (object) $row; + return (object)$row; case \PDO::FETCH_BOUND: if ($this->result->isOrdinalArray($this->boundColumns)) { foreach ($this->boundColumns as &$column) { @@ -205,8 +211,8 @@ private function processFetchedRow(array $row, ?int $fetchMode): mixed } /** - * @param class-string|null $class - * @param array $constructorArgs + * @param class-string|null $class + * @param array $constructorArgs * * @return object|false * @throws ReflectionException @@ -220,7 +226,7 @@ public function fetchObject(?string $class = "stdClass", array $constructorArgs $row = $this->result->nextRow(); if ($row) { $reflect = new ReflectionClass($class); - $obj = $reflect->newInstanceArgs($constructorArgs); + $obj = $reflect->newInstanceArgs($constructorArgs); foreach ($row as $key => $val) { $obj->$key = $val; } @@ -254,31 +260,33 @@ public function errorInfo(): array public function columnCount(): int { $rows = $this->result->getRows(); - if ($rows) { + if (is_array($rows)) { $row = array_shift($rows); - return count(array_keys($row)); + if (is_array($row)) { + return count(array_keys($row)); + } } return 0; } /** - * @param int $mode - * @param null $className - * @param mixed ...$params + * @param int $mode + * @param null $className + * @param mixed ...$params * * @return bool|int */ public function setFetchMode($mode, $className = null, ...$params): bool|int { - $r = new ReflectionClass(new Pdo()); - $constants = $r->getConstants(); - $constantNames = array_keys($constants); + $r = new ReflectionClass(new Pdo()); + $constants = $r->getConstants(); + $constantNames = array_keys($constants); $allowedConstantNames = array_filter($constantNames, function ($val) { return str_starts_with($val, 'FETCH_'); }); - $allowedConstantVals = []; + $allowedConstantVals = []; foreach ($allowedConstantNames as $name) { $allowedConstantVals[] = $constants[$name]; } diff --git a/src/Result.php b/src/Result.php index e616669..c6490bf 100644 --- a/src/Result.php +++ b/src/Result.php @@ -8,7 +8,7 @@ class Result { /** - * @var array + * @var array> */ private array $rows = []; private ?bool $executionResult; @@ -24,16 +24,16 @@ class Result private array $params = []; /** - * @param array|null $rows - * @param array|null $params - * @param bool|null $executionResult + * @param array>|null $rows + * @param array|null $params + * @param bool|null $executionResult */ public function __construct(?array $rows = null, ?array $params = null, ?bool $executionResult = null) { if (is_array($rows)) { if ($params) { $this->rows[$this->stringifyParameterSet($params)] = $rows; - $this->isParameterized = true; + $this->isParameterized = true; } else { $this->rows = $rows; } @@ -43,8 +43,9 @@ public function __construct(?array $rows = null, ?array $params = null, ?bool $e } /** - * @param array $row - * @param array $params + * @param array $row + * @param array $params + * * @return void * @throws PseudoException */ @@ -68,8 +69,9 @@ public function addRow(array $row, ?array $params = null): void } /** - * @param array $params - * @param bool $parameterize + * @param array $params + * @param bool $parameterize + * * @return void */ public function setParams(array $params, bool $parameterize = false): void @@ -81,7 +83,8 @@ public function setParams(array $params, bool $parameterize = false): void } /** - * @param array $params + * @param array $params + * * @return mixed * @throws PseudoException */ @@ -112,7 +115,7 @@ public function getRows(array $params = []): mixed /** * Returns the next row if it exists, otherwise returns false * - * @param array $rows Rows to get row from + * @param array $rows Rows to get row from * * @return false|array Next row (false if it doesn't exist) */ @@ -122,7 +125,10 @@ private function getRowIfExists(array $rows): false|array return false; } - return $rows[$this->rowOffset]; + /** @var array $row */ + $row = $rows[$this->rowOffset]; + + return $row; } /** @@ -161,7 +167,7 @@ public function getInsertId(): int } /** - * @param string $errorCode + * @param string $errorCode * * @throws PseudoException */ @@ -183,7 +189,7 @@ public function getErrorCode(): string } /** - * @param string $errorInfo + * @param string $errorInfo */ public function setErrorInfo(string $errorInfo): void { @@ -209,7 +215,8 @@ public function getAffectedRowCount(): int } /** - * @param array $arr + * @param array $arr + * * @return bool */ public function isOrdinalArray(array $arr): bool @@ -244,7 +251,8 @@ public function getExecutionResult(): bool } /** - * @param array $params + * @param array $params + * * @return string */ private function stringifyParameterSet(array $params): string @@ -263,20 +271,22 @@ private function stringifyParameterSet(array $params): string } /** - * @param string $parameterKey - * @param array $row + * @param string $parameterKey + * @param array $row + * * @return void */ private function initializeParameterizedRows(string $parameterKey, array $row): void { if (empty($this->rows)) { $this->rows[$parameterKey][] = $row; - $this->isParameterized = true; + $this->isParameterized = true; } } /** - * @param array $row + * @param array $row + * * @return void * @throws PseudoException */