diff --git a/system/BaseModel.php b/system/BaseModel.php index 6b2edc43947f..1ab9a2019d8e 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -19,6 +19,7 @@ use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Database\Query; +use CodeIgniter\Database\RawSql; use CodeIgniter\DataCaster\Cast\CastInterface; use CodeIgniter\DataConverter\DataConverter; use CodeIgniter\Entity\Cast\CastInterface as EntityCastInterface; @@ -780,6 +781,67 @@ public function getInsertID() return is_numeric($this->insertID) ? (int) $this->insertID : $this->insertID; } + /** + * Validates that the primary key values are valid for update/delete/insert operations. + * Throws exception if invalid. + * + * @param int|list|RawSql|string $id + * @param bool $allowArray Whether to allow array of IDs (true for update/delete, false for insert) + * + * @throws InvalidArgumentException + */ + protected function validateID($id, bool $allowArray = true): void + { + if (is_array($id)) { + // Check if arrays are allowed + if (! $allowArray) { + throw new InvalidArgumentException( + 'Invalid primary key: only a single value is allowed, not an array.', + ); + } + + // Check for empty array + if ($id === []) { + throw new InvalidArgumentException('Invalid primary key: cannot be an empty array.'); + } + + // Validate each ID in the array recursively + foreach ($id as $key => $valueId) { + if (is_array($valueId)) { + throw new InvalidArgumentException( + sprintf('Invalid primary key at index %s: nested arrays are not allowed.', $key), + ); + } + + // Recursive call for each value (single values only in recursion) + $this->validateID($valueId, false); + } + + return; + } + + // Allow RawSql objects for complex scenarios + if ($id instanceof RawSql) { + return; + } + + // Check for invalid single values + if (in_array($id, [null, 0, '0', '', true, false], true)) { + $type = is_bool($id) ? 'boolean ' . var_export($id, true) : var_export($id, true); + + throw new InvalidArgumentException( + sprintf('Invalid primary key: %s is not allowed.', $type), + ); + } + + // Only allow int and string at this point + if (! is_int($id) && ! is_string($id)) { + throw new InvalidArgumentException( + sprintf('Invalid primary key: must be int or string, %s given.', get_debug_type($id)), + ); + } + } + /** * Inserts data into the database. If an object is provided, * it will attempt to convert it to an array. @@ -962,19 +1024,19 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch * Updates a single record in the database. If an object is provided, * it will attempt to convert it into an array. * - * @param int|list|string|null $id - * @param object|row_array|null $row + * @param int|list|RawSql|string|null $id + * @param object|row_array|null $row * * @throws ReflectionException */ public function update($id = null, $row = null): bool { - if (is_bool($id)) { - throw new InvalidArgumentException('update(): argument #1 ($id) should not be boolean.'); - } + if ($id !== null) { + if (! is_array($id)) { + $id = [$id]; + } - if (is_numeric($id) || is_string($id)) { - $id = [$id]; + $this->validateID($id); } $row = $this->transformDataToArray($row, 'update'); @@ -1091,8 +1153,8 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc /** * Deletes a single record from the database where $id matches. * - * @param int|list|string|null $id The rows primary key(s). - * @param bool $purge Allows overriding the soft deletes setting. + * @param int|list|RawSql|string|null $id The rows primary key(s). + * @param bool $purge Allows overriding the soft deletes setting. * * @return bool|string Returns a SQL string if in test mode. * @@ -1100,12 +1162,12 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc */ public function delete($id = null, bool $purge = false) { - if (is_bool($id)) { - throw new InvalidArgumentException('delete(): argument #1 ($id) should not be boolean.'); - } + if ($id !== null) { + if (! is_array($id)) { + $id = [$id]; + } - if (! in_array($id, [null, 0, '0'], true) && (is_numeric($id) || is_string($id))) { - $id = [$id]; + $this->validateID($id); } $eventData = [ diff --git a/system/Model.php b/system/Model.php index 108ff17cf849..dc3e4db94db2 100644 --- a/system/Model.php +++ b/system/Model.php @@ -311,8 +311,13 @@ protected function doInsert(array $row) // Require non-empty primaryKey when // not using auto-increment feature - if (! $this->useAutoIncrement && ! isset($row[$this->primaryKey])) { - throw DataException::forEmptyPrimaryKey('insert'); + if (! $this->useAutoIncrement) { + if (! isset($row[$this->primaryKey])) { + throw DataException::forEmptyPrimaryKey('insert'); + } + + // Validate the primary key value (arrays not allowed for insert) + $this->validateID($row[$this->primaryKey], false); } $builder = $this->builder(); @@ -368,6 +373,9 @@ protected function doInsertBatch(?array $set = null, ?bool $escape = null, int $ if (! isset($row[$this->primaryKey])) { throw DataException::forEmptyPrimaryKey('insertBatch'); } + + // Validate the primary key value + $this->validateID($row[$this->primaryKey], false); } } @@ -381,7 +389,7 @@ protected function doUpdate($id = null, $row = null): bool $builder = $this->builder(); - if (! in_array($id, [null, '', 0, '0', []], true)) { + if (is_array($id) && $id !== []) { $builder = $builder->whereIn($this->table . '.' . $this->primaryKey, $id); } @@ -409,7 +417,7 @@ protected function doDelete($id = null, bool $purge = false) $set = []; $builder = $this->builder(); - if (! in_array($id, [null, '', 0, '0', []], true)) { + if (is_array($id) && $id !== []) { $builder = $builder->whereIn($this->primaryKey, $id); } diff --git a/tests/system/Models/DeleteModelTest.php b/tests/system/Models/DeleteModelTest.php index 0294ab32aed9..9d97ca50fa74 100644 --- a/tests/system/Models/DeleteModelTest.php +++ b/tests/system/Models/DeleteModelTest.php @@ -14,6 +14,8 @@ namespace CodeIgniter\Models; use CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\RawSql; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Exceptions\ModelException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; @@ -37,6 +39,17 @@ public function testDeleteBasics(): void $this->dontSeeInDatabase('job', ['name' => 'Developer']); } + public function testDeleteWithRawSql(): void + { + $this->createModel(JobModel::class); + $this->seeInDatabase('job', ['name' => 'Developer']); + + // RawSql objects should be allowed as primary key values + $result = $this->model->delete(new RawSql('1')); + $this->assertTrue($result); + $this->dontSeeInDatabase('job', ['name' => 'Developer']); + } + public function testDeleteFail(): void { // WARNING this value will persist! take care to roll it back. @@ -152,29 +165,60 @@ public function testOnlyDeleted(): void /** * Given an explicit empty value in the WHERE condition - * When executing a soft delete + * When executing a soft delete with where() clause * Then an exception should not be thrown * + * This test uses where() so values go into WHERE clause, not through validateID(). + * * @param int|string|null $emptyValue */ - #[DataProvider('emptyPkValues')] + #[DataProvider('provideDontThrowExceptionWhenSoftDeleteConditionIsSetWithEmptyValue')] public function testDontThrowExceptionWhenSoftDeleteConditionIsSetWithEmptyValue($emptyValue): void { $this->createModel(UserModel::class); + + if ($this->db->DBDriver === 'Postgre' && in_array($emptyValue, ['', true, false], true)) { + $this->markTestSkipped('PostgreSQL does not allow empty string, true, or false for integer columns'); + } + $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); $this->model->where('id', $emptyValue)->delete(); - $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); + // Special case: true converted to 1 + if ($emptyValue === true) { + $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NOT NULL' => null]); + } else { + $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); + } + } + + /** + * Data provider for tests using where() clause. + * These values go into WHERE clause, not through validateID(). + * + * @return iterable + */ + public static function provideDontThrowExceptionWhenSoftDeleteConditionIsSetWithEmptyValue(): iterable + { + return [ + [0], + [null], + ['0'], + [''], + [true], + [false], + ]; } /** * @param int|string|null $emptyValue + * @param class-string $exception */ #[DataProvider('emptyPkValues')] - public function testThrowExceptionWhenSoftDeleteParamIsEmptyValue($emptyValue): void + public function testThrowExceptionWhenSoftDeleteParamIsEmptyValue($emptyValue, string $exception, string $exceptionMessage): void { - $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('Deletes are not allowed unless they contain a "where" or "like" clause.'); + $this->expectException($exception); + $this->expectExceptionMessage($exceptionMessage); $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); @@ -183,16 +227,17 @@ public function testThrowExceptionWhenSoftDeleteParamIsEmptyValue($emptyValue): /** * @param int|string|null $emptyValue + * @param class-string $exception */ #[DataProvider('emptyPkValues')] - public function testDontDeleteRowsWhenSoftDeleteParamIsEmpty($emptyValue): void + public function testDontDeleteRowsWhenSoftDeleteParamIsEmpty($emptyValue, string $exception, string $exceptionMessage): void { $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); try { $this->createModel(UserModel::class)->delete($emptyValue); - } catch (DatabaseException) { - // Do nothing. + } catch (DatabaseException|InvalidArgumentException) { + // Do nothing - both exceptions are expected for different values. } $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted_at IS NULL' => null]); @@ -233,15 +278,13 @@ public function testPurgeDeletedWithSoftDeleteFalse(): void /** * @param int|string|null $id + * @param class-string $exception */ #[DataProvider('emptyPkValues')] - public function testDeleteThrowDatabaseExceptionWithoutWhereClause($id): void + public function testDeleteThrowDatabaseExceptionWithoutWhereClause($id, string $exception, string $exceptionMessage): void { - // BaseBuilder throws Exception. - $this->expectException(DatabaseException::class); - $this->expectExceptionMessage( - 'Deletes are not allowed unless they contain a "where" or "like" clause.', - ); + $this->expectException($exception); + $this->expectExceptionMessage($exceptionMessage); // $useSoftDeletes = false $this->createModel(JobModel::class); @@ -251,15 +294,13 @@ public function testDeleteThrowDatabaseExceptionWithoutWhereClause($id): void /** * @param int|string|null $id + * @param class-string $exception */ #[DataProvider('emptyPkValues')] - public function testDeleteWithSoftDeleteThrowDatabaseExceptionWithoutWhereClause($id): void + public function testDeleteWithSoftDeleteThrowDatabaseExceptionWithoutWhereClause($id, string $exception, string $exceptionMessage): void { - // Model throws Exception. - $this->expectException(DatabaseException::class); - $this->expectExceptionMessage( - 'Deletes are not allowed unless they contain a "where" or "like" clause.', - ); + $this->expectException($exception); + $this->expectExceptionMessage($exceptionMessage); // $useSoftDeletes = true $this->createModel(UserModel::class); @@ -267,12 +308,77 @@ public function testDeleteWithSoftDeleteThrowDatabaseExceptionWithoutWhereClause $this->model->delete($id); } + /** + * @return iterable + */ public static function emptyPkValues(): iterable { return [ - [0], - [null], - ['0'], + 'null' => [ + null, + DatabaseException::class, + 'Deletes are not allowed unless they contain a "where" or "like" clause.', + ], + 'false' => [ + false, + InvalidArgumentException::class, + 'Invalid primary key: boolean false is not allowed.', + ], + '0 integer' => [ + 0, + InvalidArgumentException::class, + 'Invalid primary key: 0 is not allowed.', + ], + "'0' string" => [ + '0', + InvalidArgumentException::class, + "Invalid primary key: '0' is not allowed.", + ], + 'empty string' => [ + '', + InvalidArgumentException::class, + "Invalid primary key: '' is not allowed.", + ], + 'true' => [ + true, + InvalidArgumentException::class, + 'Invalid primary key: boolean true is not allowed.', + ], + 'empty array' => [ + [], + InvalidArgumentException::class, + 'Invalid primary key: cannot be an empty array.', + ], + 'nested array' => [ + [[1, 2]], + InvalidArgumentException::class, + 'Invalid primary key at index 0: nested arrays are not allowed.', + ], + 'array with null' => [ + [1, null, 3], + InvalidArgumentException::class, + 'Invalid primary key: NULL is not allowed.', + ], + 'array with 0' => [ + [1, 0, 3], + InvalidArgumentException::class, + 'Invalid primary key: 0 is not allowed.', + ], + "array with '0'" => [ + [1, '0', 3], + InvalidArgumentException::class, + "Invalid primary key: '0' is not allowed.", + ], + 'array with empty string' => [ + [1, '', 3], + InvalidArgumentException::class, + "Invalid primary key: '' is not allowed.", + ], + 'array with boolean' => [ + [1, false, 3], + InvalidArgumentException::class, + 'Invalid primary key: boolean false is not allowed.', + ], ]; } } diff --git a/tests/system/Models/InsertModelTest.php b/tests/system/Models/InsertModelTest.php index 11b079be3ab4..baf6b45ea7f0 100644 --- a/tests/system/Models/InsertModelTest.php +++ b/tests/system/Models/InsertModelTest.php @@ -15,9 +15,11 @@ use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Entity\Entity; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use CodeIgniter\Model; use Config\Database; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use stdClass; use Tests\Support\Entity\User; @@ -407,4 +409,108 @@ public function testInsertBatchWithCasts(): void $this->seeInDatabase('user', ['email' => json_encode($userData[0]['email'])]); $this->seeInDatabase('user', ['email' => json_encode($userData[1]['email'])]); } + + /** + * @param mixed $invalidKey + * @param class-string $exception + */ + #[DataProvider('provideInvalidPrimaryKeyValues')] + public function testInsertWithInvalidPrimaryKeyWhenAutoIncrementDisabled( + $invalidKey, + string $exception, + string $exceptionMessage, + ): void { + $this->expectException($exception); + $this->expectExceptionMessage($exceptionMessage); + + $insert = [ + 'key' => $invalidKey, + 'value' => 'some value', + ]; + + $this->createModel(WithoutAutoIncrementModel::class)->insert($insert); + } + + /** + * @param mixed $invalidKey + * @param class-string $exception + */ + #[DataProvider('provideInvalidPrimaryKeyValues')] + public function testInsertBatchWithInvalidPrimaryKeyWhenAutoIncrementDisabled($invalidKey, string $exception, string $exceptionMessage): void + { + $this->expectException($exception); + $this->expectExceptionMessage($exceptionMessage); + + $insertData = [ + [ + 'key' => 'valid_key_1', + 'value' => 'value1', + ], + [ + 'key' => $invalidKey, // Invalid key in second row + 'value' => 'value2', + ], + ]; + + $this->createModel(WithoutAutoIncrementModel::class)->insertBatch($insertData); + } + + /** + * @return iterable + */ + public static function provideInvalidPrimaryKeyValues(): iterable + { + return [ + 'null' => [ + null, + DataException::class, + 'There is no primary key defined when trying to make insert', + ], + '0 integer' => [ + 0, + InvalidArgumentException::class, + 'Invalid primary key: 0 is not allowed.', + ], + "'0' string" => [ + '0', + InvalidArgumentException::class, + "Invalid primary key: '0' is not allowed.", + ], + 'empty string' => [ + '', + InvalidArgumentException::class, + "Invalid primary key: '' is not allowed.", + ], + 'true' => [ + true, + InvalidArgumentException::class, + 'Invalid primary key: boolean true is not allowed.', + ], + 'false' => [ + false, + InvalidArgumentException::class, + 'Invalid primary key: boolean false is not allowed.', + ], + 'array with null' => [ + [null], + InvalidArgumentException::class, + 'Invalid primary key: only a single value is allowed, not an array.', + ], + 'array with 0 integer' => [ + [0], + InvalidArgumentException::class, + 'Invalid primary key: only a single value is allowed, not an array.', + ], + "array with '0' string" => [ + ['0'], + InvalidArgumentException::class, + 'Invalid primary key: only a single value is allowed, not an array.', + ], + 'array with empty array' => [ + [[]], + InvalidArgumentException::class, + 'Invalid primary key: only a single value is allowed, not an array.', + ], + ]; + } } diff --git a/tests/system/Models/UpdateModelTest.php b/tests/system/Models/UpdateModelTest.php index a9380f34c771..94f8df12ea73 100644 --- a/tests/system/Models/UpdateModelTest.php +++ b/tests/system/Models/UpdateModelTest.php @@ -15,6 +15,7 @@ use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Exceptions\DataException; +use CodeIgniter\Database\RawSql; use CodeIgniter\Entity\Entity; use CodeIgniter\Exceptions\InvalidArgumentException; use Config\Database; @@ -107,6 +108,16 @@ public function testUpdateArray(): void $this->seeInDatabase('user', ['id' => 2, 'name' => 'Foo Bar']); } + public function testUpdateWithRawSql(): void + { + $this->createModel(UserModel::class); + + // RawSql objects should be allowed as primary key values + $result = $this->model->update(new RawSql('1'), ['name' => 'RawSql User']); + $this->assertTrue($result); + $this->seeInDatabase('user', ['id' => 1, 'name' => 'RawSql User']); + } + public function testUpdateResultFail(): void { // WARNING this value will persist! take care to roll it back. @@ -550,7 +561,8 @@ public function testUpdateWithSetAndEscape(): void } /** - * @param false|null $id + * @param bool|int|string|null $id + * @param class-string $exception */ #[DataProvider('provideUpdateThrowDatabaseExceptionWithoutWhereClause')] public function testUpdateThrowDatabaseExceptionWithoutWhereClause($id, string $exception, string $exceptionMessage): void @@ -561,21 +573,79 @@ public function testUpdateThrowDatabaseExceptionWithoutWhereClause($id, string $ // $useSoftDeletes = false $this->createModel(JobModel::class); - $this->model->update($id, ['name' => 'Foo Bar']); // @phpstan-ignore argument.type + $this->model->update($id, ['name' => 'Foo Bar']); } + /** + * @return iterable + */ public static function provideUpdateThrowDatabaseExceptionWithoutWhereClause(): iterable { yield from [ - [ + 'null' => [ null, DatabaseException::class, 'Updates are not allowed unless they contain a "where" or "like" clause.', ], - [ + 'false' => [ false, InvalidArgumentException::class, - 'update(): argument #1 ($id) should not be boolean.', + 'Invalid primary key: boolean false is not allowed.', + ], + 'true' => [ + true, + InvalidArgumentException::class, + 'Invalid primary key: boolean true is not allowed.', + ], + '0 integer' => [ + 0, + InvalidArgumentException::class, + 'Invalid primary key: 0 is not allowed.', + ], + "'0' string" => [ + '0', + InvalidArgumentException::class, + "Invalid primary key: '0' is not allowed.", + ], + 'empty string' => [ + '', + InvalidArgumentException::class, + "Invalid primary key: '' is not allowed.", + ], + 'empty array' => [ + [], + InvalidArgumentException::class, + 'Invalid primary key: cannot be an empty array.', + ], + 'nested array' => [ + [[1, 2]], + InvalidArgumentException::class, + 'Invalid primary key at index 0: nested arrays are not allowed.', + ], + 'array with null' => [ + [1, null, 3], + InvalidArgumentException::class, + 'Invalid primary key: NULL is not allowed.', + ], + 'array with 0' => [ + [1, 0, 3], + InvalidArgumentException::class, + 'Invalid primary key: 0 is not allowed.', + ], + "array with '0'" => [ + [1, '0', 3], + InvalidArgumentException::class, + "Invalid primary key: '0' is not allowed.", + ], + 'array with empty string' => [ + [1, '', 3], + InvalidArgumentException::class, + "Invalid primary key: '' is not allowed.", + ], + 'array with boolean' => [ + [1, false, 3], + InvalidArgumentException::class, + 'Invalid primary key: boolean false is not allowed.', ], ]; } diff --git a/user_guide_src/source/changelogs/v4.7.0.rst b/user_guide_src/source/changelogs/v4.7.0.rst index 70b27b15a600..05b667d68fda 100644 --- a/user_guide_src/source/changelogs/v4.7.0.rst +++ b/user_guide_src/source/changelogs/v4.7.0.rst @@ -40,6 +40,46 @@ The ``insertBatch()`` and ``updateBatch()`` methods now honor model settings lik ``updateOnlyChanged`` and ``allowEmptyInserts``. This change ensures consistent handling across all insert/update operations. +Primary Key Validation +^^^^^^^^^^^^^^^^^^^^^^ + +The ``insert()`` and ``insertBatch()`` (when ``useAutoIncrement`` is disabled), ``update()``, +and ``delete()`` methods now validate primary key values **before** executing database queries. +Invalid primary key values will now throw ``InvalidArgumentException`` instead of +``DatabaseException``. + +**What changed:** + +- **Exception type:** Invalid primary keys now throw ``InvalidArgumentException`` with + specific error messages instead of generic ``DatabaseException`` from the database layer. +- **Validation timing:** + + - For ``update()`` and ``delete()``: Validation happens **before** the ``beforeUpdate``/``beforeDelete`` + events and before any database queries are executed. + - For ``insert()`` and ``insertBatch()`` (when auto-increment is disabled): Validation happens + **after** the ``beforeInsert`` event but **before** database queries are executed. + +- **Invalid values:** The following values are now explicitly rejected as primary keys: + + - ``null`` (for insert when auto-increment is disabled) + - ``0`` (integer zero) + - ``'0'`` (string zero) + - ``''`` (empty string) + - ``true`` and ``false`` (booleans) + - ``[]`` (empty array) + - Nested arrays (e.g., ``[[1, 2]]``) + - Arrays containing any of the above invalid values + + **Note:** ``RawSql`` objects are allowed as primary key values for complex scenarios + where you need to use raw SQL expressions. + +This change improves error reporting by providing specific validation messages +(e.g., "Invalid primary key: 0 is not allowed") instead of generic database errors, and +prevents invalid queries from reaching the database. + +If you need to allow some of these values for the primary key, you can override the +``validateID()`` method in your model. + Entity ------ diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index c9be9878e094..f1383b45f803 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -648,6 +648,19 @@ converted to strings with the format defined in ``dateFormat['datetime']`` and .. note:: Prior to v4.5.0, the date/time formats were hard coded as ``Y-m-d H:i:s`` and ``Y-m-d`` in the Model class. +Primary Key Validation +---------------------- + +.. versionadded:: 4.7.0 + +The ``insert()``, ``insertBatch()`` (when `$useAutoIncrement`_ is ``false``), ``update()``, +and ``delete()`` methods validate primary key values before executing database queries. +Invalid values such as ``null``, ``0``, ``'0'``, empty strings, booleans, empty arrays, +or nested arrays will throw an ``InvalidArgumentException`` with a specific error message. + +If you need to customize this behavior (e.g., to allow ``0`` as a valid primary key for +legacy systems), you can override the ``validateID()`` method in your model. + Deleting Data ============= diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 50220a8e8989..611842cc1be2 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 2708 errors +# total 2706 errors includes: - argument.type.neon diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index 9d944d2ed357..b7be6baeffff 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 1320 errors +# total 1318 errors parameters: ignoreErrors: @@ -5797,11 +5797,6 @@ parameters: count: 1 path: ../../tests/system/I18n/TimeTest.php - - - message: '#^Method CodeIgniter\\Models\\DeleteModelTest\:\:emptyPkValues\(\) return type has no value type specified in iterable type iterable\.$#' - count: 1 - path: ../../tests/system/Models/DeleteModelTest.php - - message: '#^Method CodeIgniter\\Models\\FindModelTest\:\:provideAggregateAndGroupBy\(\) return type has no value type specified in iterable type iterable\.$#' count: 1 @@ -5822,11 +5817,6 @@ parameters: count: 1 path: ../../tests/system/Models/TimestampModelTest.php - - - message: '#^Method CodeIgniter\\Models\\UpdateModelTest\:\:provideUpdateThrowDatabaseExceptionWithoutWhereClause\(\) return type has no value type specified in iterable type iterable\.$#' - count: 1 - path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Method CodeIgniter\\Publisher\\PublisherRestrictionsTest\:\:provideDefaultPublicRestrictions\(\) return type has no value type specified in iterable type iterable\.$#' count: 1 diff --git a/utils/phpstan-baseline/missingType.property.neon b/utils/phpstan-baseline/missingType.property.neon index 48a8f1e1f48d..139386d26a14 100644 --- a/utils/phpstan-baseline/missingType.property.neon +++ b/utils/phpstan-baseline/missingType.property.neon @@ -243,82 +243,82 @@ parameters: path: ../../tests/system/Database/Live/MySQLi/NumberNativeTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$_options has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$_options has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$country has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$country has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$created_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$created_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$deleted has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$deleted has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$email has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$email has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$id has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$id has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$name has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$name has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:194\:\:\$updated_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:196\:\:\$updated_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$_options has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$_options has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$country has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$country has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$created_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$created_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$deleted has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$deleted has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$email has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$email has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$id has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$id has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$name has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$name has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:288\:\:\$updated_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/InsertModelTest\.php\:290\:\:\$updated_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/InsertModelTest.php @@ -393,122 +393,122 @@ parameters: path: ../../tests/system/Models/SaveModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$_options has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$_options has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$country has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$country has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$created_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$created_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$deleted has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$deleted has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$email has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$email has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$id has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$id has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$name has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$name has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:199\:\:\$updated_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:210\:\:\$updated_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$_options has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$_options has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$country has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$country has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$created_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$created_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$deleted has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$deleted has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$email has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$email has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$id has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$id has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$name has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$name has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:218\:\:\$updated_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:229\:\:\$updated_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$_options has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$_options has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$country has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$country has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$created_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$created_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$deleted has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$deleted has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$email has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$email has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$id has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$id has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$name has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$name has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php - - message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:353\:\:\$updated_at has no type specified\.$#' + message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$updated_at has no type specified\.$#' count: 1 path: ../../tests/system/Models/UpdateModelTest.php