diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f69c650..942db080d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Enh #801: Deprecate `SchemaInterface::isReadQuery()` and add `DbStringHelper::isReadQuery()` method (@Tigrov) - Enh #801: Remove unnecessary symbol `\\` from `rtrim()` function inside `DbStringHelper::baseName()` method (@Tigrov) - Bug #801: Fix bug with `Quoter::$tablePrefix` when change `AbstractConnection::$tablePrefix` property (@Tigrov) +- Enh #809: Add psalm type for parameters to bind to the SQL statement (@vjik) - Enh #810: Add more specific psalm type for `QueryFunctionsInterface::count()` result (@vjik) ## 1.2.0 November 12, 2023 diff --git a/src/Command/CommandInterface.php b/src/Command/CommandInterface.php index 3caad60d2..41cd82478 100644 --- a/src/Command/CommandInterface.php +++ b/src/Command/CommandInterface.php @@ -7,6 +7,7 @@ use Closure; use JsonException; use Throwable; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidArgumentException; use Yiisoft\Db\Exception\InvalidCallException; @@ -19,7 +20,9 @@ /** * This interface represents a database command, such as a `SELECT`, `INSERT`, `UPDATE`, or `DELETE` statement. * - * A command instance is usually created by calling {@see \Yiisoft\Db\Connection\ConnectionInterface::createCommand()}. + * A command instance is usually created by calling {@see ConnectionInterface::createCommand}. + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface CommandInterface { @@ -358,6 +361,8 @@ public function createView(string $viewName, QueryInterface|string $subQuery): s * @throws Exception * @throws InvalidArgumentException * + * @psalm-param ParamsType $params + * * Note: The method will quote the `table` parameter before using it in the generated SQL. */ public function delete(string $table, array|string $condition = '', array $params = []): static; @@ -782,6 +787,8 @@ public function truncateTable(string $table): static; * @throws Exception * @throws InvalidArgumentException * + * @psalm-param ParamsType $params + * * Note: The method will quote the `table` and `columns` parameter before using it in the generated SQL. */ public function update(string $table, array $columns, array|string $condition = '', array $params = []): static; @@ -823,6 +830,7 @@ public function update(string $table, array $columns, array|string $condition = * @throws NotSupportedException * * @psalm-param array|QueryInterface $insertColumns + * @psalm-param ParamsType $params * * Note: The method will quote the `table` and `insertColumns`, `updateColumns` parameters before using it in the * generated SQL. diff --git a/src/Connection/ConnectionInterface.php b/src/Connection/ConnectionInterface.php index 596a5df24..a8aad3d1c 100644 --- a/src/Connection/ConnectionInterface.php +++ b/src/Connection/ConnectionInterface.php @@ -25,6 +25,8 @@ * * It allows you to access and manipulate databases in a database-agnostic way, so you can write code that works with * different database systems without having to worry about the specific details of each one. + * + * @psalm-type ParamsType = array|list */ interface ConnectionInterface { @@ -64,6 +66,8 @@ public function createBatchQueryResult(QueryInterface $query, bool $each = false * @throws InvalidConfigException * * @return CommandInterface The database command instance. + * + * @psalm-param ParamsType $params */ public function createCommand(string $sql = null, array $params = []): CommandInterface; diff --git a/src/Expression/Expression.php b/src/Expression/Expression.php index 431e1cc08..174f66399 100644 --- a/src/Expression/Expression.php +++ b/src/Expression/Expression.php @@ -5,6 +5,7 @@ namespace Yiisoft\Db\Expression; use Stringable; +use Yiisoft\Db\Connection\ConnectionInterface; /** * Represents a DB expression that doesn't need escaping or quoting. @@ -22,9 +23,14 @@ * * Expression objects are mainly created for passing raw SQL expressions to methods of * {@see \Yiisoft\Db\Query\QueryInterface} and related classes. + * + * @psalm-import-type ParamsType from ConnectionInterface */ class Expression implements ExpressionInterface, Stringable { + /** + * @psalm-param ParamsType $params + */ public function __construct(private string $expression, private array $params = []) { } @@ -40,6 +46,8 @@ public function __toString(): string /** * @return array List of parameters to bind to this expression. The keys are placeholders appearing in * {@see expression} and the values are the corresponding parameter values. + * + * @psalm-return ParamsType */ public function getParams(): array { diff --git a/src/Query/Query.php b/src/Query/Query.php index e262ac581..8e1e49f06 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -131,10 +131,6 @@ public function addParams(array $params): static if (empty($this->params)) { $this->params = $params; } else { - /** - * @psalm-var array $params - * @psalm-var mixed $value - */ foreach ($params as $name => $value) { if (is_int($name)) { $this->params[] = $value; diff --git a/src/Query/QueryInterface.php b/src/Query/QueryInterface.php index 0bbe54957..246d007bd 100644 --- a/src/Query/QueryInterface.php +++ b/src/Query/QueryInterface.php @@ -7,6 +7,7 @@ use Closure; use Throwable; use Yiisoft\Db\Command\CommandInterface; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidArgumentException; use Yiisoft\Db\Exception\InvalidConfigException; @@ -25,6 +26,8 @@ * Allows pagination via {@see limit()} and {@see offset()}. * * Sorting is supported via {@see orderBy()} and items can be limited to match some conditions using {@see where()}. + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface QueryInterface extends ExpressionInterface, QueryPartsInterface, QueryFunctionsInterface { @@ -34,6 +37,8 @@ interface QueryInterface extends ExpressionInterface, QueryPartsInterface, Query * @param array $params The list of query parameter values indexed by parameter placeholders. * For example, `[':name' => 'Dan', ':age' => 31]`. * + * @psalm-param ParamsType $params + * * @see params() */ public function addParams(array $params): static; @@ -260,6 +265,8 @@ public function one(): array|null; * @param array $params List of query parameter values indexed by parameter placeholders. * For example, `[':name' => 'Dan', ':age' => 31]`. * + * @psalm-param ParamsType $params + * * @see addParams() */ public function params(array $params): static; diff --git a/src/Query/QueryPartsInterface.php b/src/Query/QueryPartsInterface.php index f6b9ca974..9d2b4dec2 100644 --- a/src/Query/QueryPartsInterface.php +++ b/src/Query/QueryPartsInterface.php @@ -5,6 +5,7 @@ namespace Yiisoft\Db\Query; use Closure; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\ExpressionInterface; @@ -13,6 +14,8 @@ * query, such as the {@see addGroupBy()}, {@see addSelect()}, {@see addOrderBy()}, {@see andFilterCompare()}, etc. * * {@see Query} uses these methods to build and manipulate SQL statements. + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface QueryPartsInterface { @@ -126,6 +129,8 @@ public function andFilterHaving(array $condition): static; * Please refer to {@see where()} on how to specify this parameter. * @param array $params The parameters (name => value) to be bound to the query. * + * @psalm-param ParamsType $params + * * @see having() * @see orHaving() */ @@ -160,6 +165,8 @@ public function andFilterWhere(array $condition): static; * Please refer to {@see where()} on how to specify this parameter. * @param array $params The parameters (name => value) to be bound to the query. * + * @psalm-param ParamsType $params + * * @see where() * @see orWhere() */ @@ -293,6 +300,8 @@ public function groupBy(array|string|ExpressionInterface $columns): static; * Please refer to {@see where()} on how to specify this parameter. * @param array $params The parameters (name => value) to bind to the query. * + * @psalm-param ParamsType $params + * * @see andHaving() * @see orHaving() */ @@ -329,6 +338,8 @@ public function indexBy(string|Closure|null $column): static; * @param array|string $on The join condition that should appear in the ON part. Please refer to {@see join()} on * how to specify this parameter. * @param array $params The parameters (name => value) to bind to the query. + * + * @psalm-param ParamsType $params */ public function innerJoin(array|string $table, array|string $on = '', array $params = []): static; @@ -357,6 +368,8 @@ public function innerJoin(array|string $table, array|string $on = '', array $par * 'post.author_id = user.id' * ``` * @param array $params The parameters (name => value) to bind to the query. + * + * @psalm-param ParamsType $params */ public function join(string $type, array|string $table, array|string $on = '', array $params = []): static; @@ -374,6 +387,8 @@ public function join(string $type, array|string $table, array|string $on = '', a * @param array|string $on The join condition that should appear in the ON part. Please refer to {@see join()} on * how to specify this parameter. * @param array $params The parameters (name => value) to bind to the query. + * + * @psalm-param ParamsType $params */ public function leftJoin(array|string $table, array|string $on = '', array $params = []): static; @@ -454,6 +469,8 @@ public function orFilterHaving(array $condition): static; * Please refer to {@see where()} on how to specify this parameter. * @param array $params The parameters (name => value) to bind to the query. * + * @psalm-param ParamsType $params + * * @see having() * @see andHaving() */ @@ -468,6 +485,8 @@ public function orHaving(array|string|ExpressionInterface $condition, array $par * Please refer to {@see where()} on how to specify this parameter. * @param array $params The parameters (name => value) to bind to the query. * + * @psalm-param ParamsType $params + * * @see where() * @see andWhere() */ @@ -487,6 +506,8 @@ public function orWhere(array|string|ExpressionInterface $condition, array $para * @param array|string $on The join condition that should appear in the ON part. * Please refer to {@see join()} on how to specify this parameter. * @param array $params The parameters (name => value) to be bound to the query. + * + * @psalm-param ParamsType $params */ public function rightJoin(array|string $table, array|string $on = '', array $params = []): static; @@ -636,6 +657,8 @@ public function union(QueryInterface|string $sql, bool $all = false): static; * @param array|ExpressionInterface|string|null $condition The conditions to put in the `WHERE` part. * @param array $params The parameters (name => value) to bind to the query. * + * @psalm-param ParamsType $params + * * @see andWhere() * @see orWhere() */ diff --git a/src/QueryBuilder/AbstractDMLQueryBuilder.php b/src/QueryBuilder/AbstractDMLQueryBuilder.php index dc048eb2c..302153c6b 100644 --- a/src/QueryBuilder/AbstractDMLQueryBuilder.php +++ b/src/QueryBuilder/AbstractDMLQueryBuilder.php @@ -6,6 +6,7 @@ use JsonException; use Traversable; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Constraint\Constraint; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidArgumentException; @@ -47,6 +48,8 @@ * tables and modifying existing data. * * @link https://en.wikipedia.org/wiki/Data_manipulation_language + * + * @psalm-import-type ParamsType from ConnectionInterface */ abstract class AbstractDMLQueryBuilder implements DMLQueryBuilderInterface { @@ -136,6 +139,8 @@ public function upsert( * @param array $params The binding parameters that will be generated by this method. * * @return string[] The values. + * + * @psalm-param ParamsType $params */ protected function prepareBatchInsertValues(string $table, iterable $rows, array $columns, array &$params): array { @@ -220,6 +225,8 @@ protected function extractColumnNames(iterable $rows, array $columns): array * @throws NotSupportedException * * @return array Array of quoted column names, values, and params. + * + * @psalm-param ParamsType $params * @psalm-return array{0: string[], 1: string, 2: array} */ protected function prepareInsertSelectSubQuery(QueryInterface $columns, array $params = []): array @@ -263,6 +270,8 @@ protected function prepareInsertSelectSubQuery(QueryInterface $columns, array $p * @throws NotSupportedException * * @return array Array of quoted column names, placeholders, values, and params. + * + * @psalm-param ParamsType $params * @psalm-return array{0: string[], 1: string[], 2: string, 3: array} */ protected function prepareInsertValues(string $table, array|QueryInterface $columns, array $params = []): array @@ -306,6 +315,7 @@ protected function prepareInsertValues(string $table, array|QueryInterface $colu * @throws InvalidArgumentException * @throws NotSupportedException * + * @psalm-param ParamsType $params * @psalm-return array{0: string[], 1: array} */ protected function prepareUpdateSets(string $table, array $columns, array $params = []): array diff --git a/src/QueryBuilder/DMLQueryBuilderInterface.php b/src/QueryBuilder/DMLQueryBuilderInterface.php index fdac752aa..24aa4c856 100644 --- a/src/QueryBuilder/DMLQueryBuilderInterface.php +++ b/src/QueryBuilder/DMLQueryBuilderInterface.php @@ -5,6 +5,7 @@ namespace Yiisoft\Db\QueryBuilder; use JsonException; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidArgumentException; use Yiisoft\Db\Exception\InvalidConfigException; @@ -15,6 +16,8 @@ * Defines methods for building SQL statements for DML (data manipulation language). * * @link https://en.wikipedia.org/wiki/Data_manipulation_language + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface DMLQueryBuilderInterface { @@ -43,6 +46,7 @@ interface DMLQueryBuilderInterface * * @psalm-param string[] $columns * @psalm-param iterable> $rows + * @psalm-param ParamsType $params * * Note: * - That the values in each row must match the corresponding column names. @@ -71,6 +75,8 @@ public function batchInsert(string $table, array $columns, iterable $rows, array * * @return string The `DELETE` SQL. * + * @psalm-param ParamsType $params + * * Note: The method will escape the table and column names. */ public function delete(string $table, array|string $condition, array &$params): string; @@ -101,6 +107,8 @@ public function delete(string $table, array|string $condition, array &$params): * * @return string The INSERT SQL. * + * @psalm-param ParamsType $params + * * Note: The method will escape the table and column names. */ public function insert(string $table, QueryInterface|array $columns, array &$params = []): string; @@ -116,6 +124,8 @@ public function insert(string $table, QueryInterface|array $columns, array &$par * @throws Exception * @throws NotSupportedException If this isn't supported by the underlying DBMS. * + * @psalm-param ParamsType $params + * * Note: The method will escape the table and column names. */ public function insertWithReturningPks(string $table, QueryInterface|array $columns, array &$params = []): string; @@ -160,6 +170,8 @@ public function resetSequence(string $table, int|string|null $value = null): str * * @return string The UPDATE SQL. * + * @psalm-param ParamsType $params + * * Note: The method will escape the table and column names. */ public function update(string $table, array $columns, array|string $condition, array &$params = []): string; @@ -195,6 +207,7 @@ public function update(string $table, array $columns, array|string $condition, a * @throws NotSupportedException If this isn't supported by the underlying DBMS. * * @psalm-param array|QueryInterface $insertColumns + * @psalm-param ParamsType $params * * Note: The method will escape the table and column names. */ diff --git a/src/QueryBuilder/DQLQueryBuilderInterface.php b/src/QueryBuilder/DQLQueryBuilderInterface.php index 759c91150..dd2076dbb 100644 --- a/src/QueryBuilder/DQLQueryBuilderInterface.php +++ b/src/QueryBuilder/DQLQueryBuilderInterface.php @@ -4,6 +4,7 @@ namespace Yiisoft\Db\QueryBuilder; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidArgumentException; use Yiisoft\Db\Exception\InvalidConfigException; @@ -17,6 +18,8 @@ * Defines methods for building SQL statements for DQL (data query language). * * @link https://en.wikipedia.org/wiki/Data_query_language + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface DQLQueryBuilderInterface { @@ -37,6 +40,7 @@ interface DQLQueryBuilderInterface * @return array The generated SQL statement (the first array element) and the corresponding parameters to bind * to the SQL statement (the second array element). The parameters returned include those provided in `$params`. * + * @psalm-param ParamsType $params * @psalm-return array{0: string, 1: array} */ public function build(QueryInterface $query, array $params = []): array; @@ -64,6 +68,8 @@ public function buildColumns(array|string $columns): string; * @throws InvalidArgumentException * @throws InvalidConfigException * @throws NotSupportedException + * + * @psalm-param ParamsType $params */ public function buildCondition(array|string|ExpressionInterface|null $condition, array &$params = []): string; @@ -82,6 +88,8 @@ public function buildCondition(array|string|ExpressionInterface|null $condition, * * @return string The SQL statement that won't be neither quoted nor encoded before passing to DBMS. * + * @psalm-param ParamsType $params + * * @see ExpressionInterface * @see ExpressionBuilderInterface * @see AbstractDQLQueryBuilder::expressionBuilders @@ -97,6 +105,8 @@ public function buildExpression(ExpressionInterface $expression, array &$params * @throws NotSupportedException * * @return string The `FROM` clause built from {@see \Yiisoft\Db\Query\Query::from()}. + * + * @psalm-param ParamsType $params */ public function buildFrom(array|null $tables, array &$params): string; @@ -110,6 +120,8 @@ public function buildFrom(array|null $tables, array &$params): string; * @throws InvalidArgumentException * * @return string The `GROUP BY` clause + * + * @psalm-param ParamsType $params */ public function buildGroupBy(array $columns, array &$params = []): string; @@ -123,6 +135,8 @@ public function buildGroupBy(array $columns, array &$params = []): string; * @throws NotSupportedException * * @return string The `HAVING` clause built from {@see \Yiisoft\Db\Query\Query::having()}. + * + * @psalm-param ParamsType $params */ public function buildHaving(array|ExpressionInterface|string|null $condition, array &$params = []): string; @@ -133,6 +147,8 @@ public function buildHaving(array|ExpressionInterface|string|null $condition, ar * @throws Exception If the `$joins` parameter isn't in proper format. * * @return string The `JOIN` clause built from {@see \Yiisoft\Db\Query\Query::join()}. + * + * @psalm-param ParamsType $params */ public function buildJoin(array $joins, array &$params): string; @@ -161,6 +177,8 @@ public function buildLimit(ExpressionInterface|int|null $limit, ExpressionInterf * @throws InvalidArgumentException * * @return string The `ORDER BY` clause built from {@see \Yiisoft\Db\Query\Query::orderBy()}. + * + * @psalm-param ParamsType $params */ public function buildOrderBy(array $columns, array &$params = []): string; @@ -180,6 +198,8 @@ public function buildOrderBy(array $columns, array &$params = []): string; * @throws InvalidArgumentException * * @return string The SQL completed with `ORDER BY/LIMIT/OFFSET` (if any). + * + * @psalm-param ParamsType $params */ public function buildOrderByAndLimit( string $sql, @@ -203,6 +223,8 @@ public function buildOrderByAndLimit( * @throws NotSupportedException * * @return string The `SELECT` clause built from {@see \Yiisoft\Db\Query\Query::select()}. + * + * @psalm-param ParamsType $params */ public function buildSelect( array $columns, @@ -221,6 +243,8 @@ public function buildSelect( * @throws NotSupportedException * * @return string The `UNION` clause built from {@see \Yiisoft\Db\Query\Query::union()}. + * + * @psalm-param ParamsType $params */ public function buildUnion(array $unions, array &$params): string; @@ -235,6 +259,8 @@ public function buildUnion(array $unions, array &$params): string; * @throws NotSupportedException * * @return string The `WHERE` clause built from {@see \Yiisoft\Db\Query\Query::where()}. + * + * @psalm-param ParamsType $params */ public function buildWhere( array|string|ConditionInterface|ExpressionInterface|null $condition, @@ -251,6 +277,8 @@ public function buildWhere( * @throws NotSupportedException * * @return string The `WITH` clause built from {@see \Yiisoft\Db\Query\Query::with}. + * + * @psalm-param ParamsType $params */ public function buildWithQueries(array $withs, array &$params): string; diff --git a/src/QueryBuilder/QueryBuilderInterface.php b/src/QueryBuilder/QueryBuilderInterface.php index 065f4c164..0551b0d73 100644 --- a/src/QueryBuilder/QueryBuilderInterface.php +++ b/src/QueryBuilder/QueryBuilderInterface.php @@ -4,6 +4,7 @@ namespace Yiisoft\Db\QueryBuilder; +use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Exception\InvalidArgumentException; use Yiisoft\Db\Expression\ExpressionBuilderInterface; use Yiisoft\Db\Expression\ExpressionInterface; @@ -19,6 +20,8 @@ * A query builder should also support creating SQL statements for DBMS-specific features such as creating indexes, * adding foreign keys, etc. through the methods defined in {@see DDLQueryBuilderInterface}, {@see DMLQueryBuilderInterface} * and {@see DQLQueryBuilderInterface}. + * + * @psalm-import-type ParamsType from ConnectionInterface */ interface QueryBuilderInterface extends DDLQueryBuilderInterface, DMLQueryBuilderInterface, DQLQueryBuilderInterface { @@ -28,6 +31,8 @@ interface QueryBuilderInterface extends DDLQueryBuilderInterface, DMLQueryBuilde * @param array $params Passed by reference. * * @return string The placeholder name in $params array. + * + * @psalm-param ParamsType $params */ public function bindParam(mixed $value, array &$params = []): string;