Merge pull request #9358 from codeigniter4/develop

4.5.7 Ready code
This commit is contained in:
John Paul E. Balandan, CPA 2024-12-31 20:09:58 +08:00 committed by GitHub
commit daf65f9519
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 315 additions and 180 deletions

View File

@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links:
- name: CodeIgniter Forum
url: https://forum.codeigniter.com/forum-30.html
about: Please ask your support questions in the forums. Thanks!
about: Please ask your support questions and/or feature requests in the forums. Thanks!
- name: CodeIgniter Slack channel
url: https://codeigniterchat.slack.com

View File

@ -1,11 +0,0 @@
---
name: Feature request
about: How to submit a feature request
title: ''
labels: ''
assignees: ''
---
Please submit feature requests to our [forum](https://forum.codeigniter.com/forum-29.html).
We use github issues to track bugs and planned work.

View File

@ -1,10 +1,10 @@
---
name: Planned work
about: Approved work planning
about: Maintainers' space. DO NOT use this for your bug reports!
title: 'Dev: '
labels: dev
assignees: ''
---
Repo maintainers will create "issues" for planned work, so it can be tracked.
Maintainers will create "issues" for planned work, so it can be tracked.

41
.github/mergeable.yml vendored Normal file
View File

@ -0,0 +1,41 @@
# https://mergeable.readthedocs.io/en/latest/configuration.html
version: 2
mergeable:
- when: issues.opened
validate:
- do: description
and:
- must_include:
regex: '^### PHP Version'
- must_include:
regex: '^### CodeIgniter4 Version'
- do: author
and:
- must_exclude:
regex: 'kenjis'
- must_exclude:
regex: 'lonnieezell'
- must_exclude:
regex: 'MGatner'
- must_exclude:
regex: 'michalsn'
- must_exclude:
regex: 'paulbalandan'
- must_exclude:
regex: 'samsonasik'
fail:
- do: comment
payload: |
Hi there! :wave:
It looks like you opened an issue without following the bug report template:
Bug report ([open an issue](https://github.com/codeigniter4/CodeIgniter4/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=Bug%3A+))
If you are opening a feature request or support question, please do so in the [forums](https://forum.codeigniter.com/forum-30.html).
The current issue will be closed. This is a precaution to save maintainers' time, I hope you'll understand.
Sincerely, the mergeable bot 🤖
- do: close

View File

@ -1,5 +1,17 @@
# Changelog
## [v4.5.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.5.7) (2024-12-31)
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.5.6...v4.5.7)
### Fixed Bugs
* fix: handle namespaced helper found on Common helper by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/9354
* fix: `Forge::dropColumn()` always returns `false` on SQLite3 driver by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9351
### Refactoring
* refactor: enable AddArrowFunctionReturnTypeRector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/9343
## [v4.5.6](https://github.com/codeigniter4/CodeIgniter4/tree/v4.5.6) (2024-12-28)
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.5.5...v4.5.6)

View File

@ -50,7 +50,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
$function .= $padClass . $error['function'];
}
$args = implode(', ', array_map(static fn ($value) => match (true) {
$args = implode(', ', array_map(static fn ($value): string => match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => $value !== [] ? '[...]' : '[]',
$value === null => 'null', // return the lowercased version

View File

@ -10,7 +10,7 @@
<output>api/build/</output>
<cache>api/cache/</cache>
</paths>
<version number="4.5.6">
<version number="4.5.7">
<api format="php">
<source dsn=".">
<path>system</path>

View File

@ -41,6 +41,7 @@ use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector;
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
use Rector\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector;
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
@ -201,6 +202,7 @@ return RectorConfig::configure()
TypedPropertyFromAssignsRector::class,
ClosureReturnTypeRector::class,
FlipTypeControlToUseExclusiveTypeRector::class,
AddArrowFunctionReturnTypeRector::class,
])
->withConfiguredRule(StringClassNameToClassConstantRector::class, [
// keep '\\' prefix string on string '\Foo\Bar'

View File

@ -339,7 +339,7 @@ class CLI
$pattern = preg_match_all('/^\d+(,\d+)*$/', trim($input));
// separate input by comma and convert all to an int[]
$inputToArray = array_map(static fn ($value) => (int) $value, explode(',', $input));
$inputToArray = array_map(static fn ($value): int => (int) $value, explode(',', $input));
// find max from key of $options
$maxOptions = array_key_last($options);
// find max from input

View File

@ -56,7 +56,7 @@ class CodeIgniter
/**
* The current version of CodeIgniter Framework
*/
public const CI_VERSION = '4.5.6';
public const CI_VERSION = '4.5.7';
/**
* App startup time.

View File

@ -187,7 +187,7 @@ class Routes extends BaseCommand
// Sort by Handler.
if ($sortByHandler) {
usort($tbody, static fn ($handler1, $handler2) => strcmp($handler1[3], $handler2[3]));
usort($tbody, static fn ($handler1, $handler2): int => strcmp($handler1[3], $handler2[3]));
}
if ($host !== null) {

View File

@ -598,7 +598,7 @@ if (! function_exists('helper')) {
if (str_contains($filename, '\\')) {
$path = $loader->locateFile($filename, 'Helpers');
if ($path !== '') {
if ($path === false) {
throw FileNotFoundException::forFileNotFound($filename);
}

View File

@ -2018,7 +2018,7 @@ class BaseBuilder
$sql = 'INSERT INTO ' . $table . ' (' . implode(', ', $keys) . ")\n{:_table_:}ON DUPLICATE KEY UPDATE\n" . implode(
",\n",
array_map(
static fn ($key, $value) => $table . '.' . $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $table . '.' . $key . ($value instanceof RawSql ?
' = ' . $value :
' = VALUES(' . $value . ')'),
array_keys($updateFields),
@ -2108,7 +2108,7 @@ class BaseBuilder
if (is_string($set)) {
$set = explode(',', $set);
$set = array_map(static fn ($key) => trim($key), $set);
$set = array_map(static fn ($key): string => trim($key), $set);
}
if ($set instanceof RawSql) {
@ -2152,7 +2152,7 @@ class BaseBuilder
if (is_string($query)) {
if ($columns !== null && is_string($columns)) {
$columns = explode(',', $columns);
$columns = array_map(static fn ($key) => trim($key), $columns);
$columns = array_map(static fn ($key): string => trim($key), $columns);
}
$columns = (array) $columns;
@ -2190,7 +2190,7 @@ class BaseBuilder
*/
protected function formatValues(array $values): array
{
return array_map(static fn ($index) => '(' . implode(',', $index) . ')', $values);
return array_map(static fn ($index): string => '(' . implode(',', $index) . ')', $values);
}
/**
@ -2649,7 +2649,7 @@ class BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
' = ' . $value :
' = ' . $alias . '.' . $value),
array_keys($updateFields),
@ -2691,8 +2691,8 @@ class BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),
@ -2946,8 +2946,8 @@ class BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),

View File

@ -778,7 +778,7 @@ class Forge
}
/**
* @param array|string $columnNames column names to DROP
* @param list<string>|string $columnNames column names to DROP
*
* @return bool
*
@ -861,7 +861,7 @@ class Forge
$columnNamesToDrop = explode(',', $columnNamesToDrop);
}
$columnNamesToDrop = array_map(fn ($field) => 'DROP COLUMN ' . $this->db->escapeIdentifiers(trim($field)), $columnNamesToDrop);
$columnNamesToDrop = array_map(fn ($field): string => 'DROP COLUMN ' . $this->db->escapeIdentifiers(trim($field)), $columnNamesToDrop);
return $sql . implode(', ', $columnNamesToDrop);
}

View File

@ -115,7 +115,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $table . '.' . $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $table . '.' . $key . ($value instanceof RawSql ?
' = ' . $value :
' = ' . $alias . '.' . $value),
array_keys($updateFields),
@ -132,8 +132,8 @@ class Builder extends BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),

View File

@ -89,8 +89,8 @@ class Builder extends BaseBuilder
$data = implode(
" FROM DUAL UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),
@ -107,7 +107,7 @@ class Builder extends BaseBuilder
*/
protected function _replace(string $table, array $keys, array $values): string
{
$fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys);
$fieldNames = array_map(static fn ($columnName): string => trim($columnName, '"'), $keys);
$uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool {
$hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields);
@ -126,7 +126,7 @@ class Builder extends BaseBuilder
$sql = 'MERGE INTO ' . $table . "\n USING (SELECT ";
$sql .= implode(', ', array_map(static fn ($columnName, $value) => $value . ' ' . $columnName, $keys, $values));
$sql .= implode(', ', array_map(static fn ($columnName, $value): string => $value . ' ' . $columnName, $keys, $values));
$sql .= ' FROM DUAL) "_replace" ON ( ';
@ -134,16 +134,16 @@ class Builder extends BaseBuilder
$onList[] = '1 != 1';
foreach ($uniqueIndexes as $index) {
$onList[] = '(' . implode(' AND ', array_map(static fn ($columnName) => $table . '."' . $columnName . '" = "_replace"."' . $columnName . '"', $index->fields)) . ')';
$onList[] = '(' . implode(' AND ', array_map(static fn ($columnName): string => $table . '."' . $columnName . '" = "_replace"."' . $columnName . '"', $index->fields)) . ')';
}
$sql .= implode(' OR ', $onList) . ') WHEN MATCHED THEN UPDATE SET ';
$sql .= implode(', ', array_map(static fn ($columnName) => $columnName . ' = "_replace".' . $columnName, $replaceableFields));
$sql .= implode(', ', array_map(static fn ($columnName): string => $columnName . ' = "_replace".' . $columnName, $replaceableFields));
$sql .= ' WHEN NOT MATCHED THEN INSERT (' . implode(', ', $replaceableFields) . ') VALUES ';
return $sql . (' (' . implode(', ', array_map(static fn ($columnName) => '"_replace".' . $columnName, $replaceableFields)) . ')');
return $sql . (' (' . implode(', ', array_map(static fn ($columnName): string => '"_replace".' . $columnName, $replaceableFields)) . ')');
}
/**
@ -298,7 +298,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $table . '.' . $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $table . '.' . $key . ($value instanceof RawSql ?
' = ' . $value :
' = ' . $alias . '.' . $value),
array_keys($updateFields),
@ -315,8 +315,8 @@ class Builder extends BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)) . ' FROM DUAL',
@ -342,7 +342,7 @@ class Builder extends BaseBuilder
$constraints = $this->QBOptions['constraints'] ?? [];
if (empty($constraints)) {
$fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys);
$fieldNames = array_map(static fn ($columnName): string => trim($columnName, '"'), $keys);
$uniqueIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool {
$hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields);
@ -401,7 +401,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
" = {$value}" :
" = {$alias}.{$value}"),
array_keys($updateFields),
@ -412,7 +412,7 @@ class Builder extends BaseBuilder
$sql .= "\nWHEN NOT MATCHED THEN INSERT (" . implode(', ', $keys) . ")\nVALUES ";
$sql .= (' ('
. implode(', ', array_map(static fn ($columnName) => "{$alias}.{$columnName}", $keys))
. implode(', ', array_map(static fn ($columnName): string => "{$alias}.{$columnName}", $keys))
. ')');
$this->QBOptions['sql'] = $sql;
@ -424,8 +424,8 @@ class Builder extends BaseBuilder
$data = implode(
" FROM DUAL UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),
@ -503,8 +503,8 @@ class Builder extends BaseBuilder
$data = implode(
" FROM DUAL UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),

View File

@ -37,7 +37,7 @@ class Result extends BaseResult
*/
public function getFieldNames(): array
{
return array_map(fn ($fieldIndex) => oci_field_name($this->resultID, $fieldIndex), range(1, $this->getFieldCount()));
return array_map(fn ($fieldIndex): false|string => oci_field_name($this->resultID, $fieldIndex), range(1, $this->getFieldCount()));
}
/**

View File

@ -353,7 +353,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
' = ' . $value :
' = ' . $that->cast($alias . '.' . $value, $that->getFieldType($table, $key))),
array_keys($updateFields),
@ -394,8 +394,8 @@ class Builder extends BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),
@ -458,7 +458,7 @@ class Builder extends BaseBuilder
// if this is the first iteration of batch then we need to build skeleton sql
if ($sql === '') {
$fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys);
$fieldNames = array_map(static fn ($columnName): string => trim($columnName, '"'), $keys);
$constraints = $this->QBOptions['constraints'] ?? [];
@ -524,7 +524,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
" = {$value}" :
" = {$alias}.{$value}"),
array_keys($updateFields),
@ -617,8 +617,8 @@ class Builder extends BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),

View File

@ -371,7 +371,7 @@ class Connection extends BaseConnection
$obj = new stdClass();
$obj->name = $row->indexname;
$_fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef)));
$obj->fields = array_map(static fn ($v) => trim($v), $_fields);
$obj->fields = array_map(static fn ($v): string => trim($v), $_fields);
if (str_starts_with($row->indexdef, 'CREATE UNIQUE INDEX pk')) {
$obj->type = 'PRIMARY';

View File

@ -418,7 +418,7 @@ class Query implements QueryInterface, Stringable
*/
$search = '/\b(?:' . implode('|', $highlight) . ')\b(?![^(&#039;)]*&#039;(?:(?:[^(&#039;)]*&#039;){2})*[^(&#039;)]*$)/';
return preg_replace_callback($search, static fn ($matches) => '<strong>' . str_replace(' ', '&nbsp;', $matches[0]) . '</strong>', $sql);
return preg_replace_callback($search, static fn ($matches): string => '<strong>' . str_replace(' ', '&nbsp;', $matches[0]) . '</strong>', $sql);
}
/**

View File

@ -694,7 +694,7 @@ class Builder extends BaseBuilder
$identityInFields = in_array($tableIdentity, $keys, true);
$fieldNames = array_map(static fn ($columnName) => trim($columnName, '"'), $keys);
$fieldNames = array_map(static fn ($columnName): string => trim($columnName, '"'), $keys);
if (empty($constraints)) {
$tableIndexes = $this->db->getIndexData($table);
@ -773,7 +773,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
' = ' . $value :
" = {$alias}.{$value}"),
array_keys($updateFields),
@ -787,7 +787,7 @@ class Builder extends BaseBuilder
'(' . implode(
', ',
array_map(
static fn ($columnName) => $columnName === $tableIdentity
static fn ($columnName): string => $columnName === $tableIdentity
? "CASE WHEN {$alias}.{$columnName} IS NULL THEN (SELECT "
. 'isnull(IDENT_CURRENT(\'' . $fullTableName . '\')+IDENT_INCR(\''
. $fullTableName . "'),1)) ELSE {$alias}.{$columnName} END"

View File

@ -257,7 +257,7 @@ class Connection extends BaseConnection
$obj->name = $row->index_name;
$_fields = explode(',', trim($row->index_keys));
$obj->fields = array_map(static fn ($v) => trim($v), $_fields);
$obj->fields = array_map(static fn ($v): string => trim($v), $_fields);
if (str_contains($row->index_description, 'primary key located on')) {
$obj->type = 'PRIMARY';

View File

@ -225,7 +225,7 @@ class Forge extends BaseForge
$sql = 'ALTER TABLE ' . $fullTable . ' DROP ';
$fields = array_map(static fn ($item) => 'COLUMN [' . trim($item) . ']', (array) $columnNamesToDrop);
$fields = array_map(static fn ($item): string => 'COLUMN [' . trim($item) . ']', (array) $columnNamesToDrop);
return $sql . implode(',', $fields);
}
@ -380,7 +380,7 @@ class Forge extends BaseForge
// https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver16#remarks
$maxLength = max(
array_map(
static fn ($value) => strlen($value),
static fn ($value): int => strlen($value),
$attributes['CONSTRAINT']
)
);

View File

@ -143,7 +143,7 @@ class Builder extends BaseBuilder
$constraints = $this->QBOptions['constraints'] ?? [];
if (empty($constraints)) {
$fieldNames = array_map(static fn ($columnName) => trim($columnName, '`'), $keys);
$fieldNames = array_map(static fn ($columnName): string => trim($columnName, '`'), $keys);
$allIndexes = array_filter($this->db->getIndexData($table), static function ($index) use ($fieldNames): bool {
$hasAllFields = count(array_intersect($index->fields, $fieldNames)) === count($index->fields);
@ -179,7 +179,7 @@ class Builder extends BaseBuilder
$sql = 'INSERT INTO ' . $table . ' (';
$sql .= implode(', ', array_map(static fn ($columnName) => $columnName, $keys));
$sql .= implode(', ', array_map(static fn ($columnName): string => $columnName, $keys));
$sql .= ")\n";
@ -192,7 +192,7 @@ class Builder extends BaseBuilder
$sql .= implode(
",\n",
array_map(
static fn ($key, $value) => $key . ($value instanceof RawSql ?
static fn ($key, $value): string => $key . ($value instanceof RawSql ?
" = {$value}" :
" = {$alias}.{$value}"),
array_keys($updateFields),
@ -265,8 +265,8 @@ class Builder extends BaseBuilder
$data = implode(
" UNION ALL\n",
array_map(
static fn ($value) => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index) => $index . ' ' . $key,
static fn ($value): string => 'SELECT ' . implode(', ', array_map(
static fn ($key, $index): string => $index . ' ' . $key,
$keys,
$value
)),

View File

@ -110,6 +110,31 @@ class Forge extends BaseForge
return true;
}
/**
* @param list<string>|string $columnNames
*
* @throws DatabaseException
*/
public function dropColumn(string $table, $columnNames): bool
{
$columns = is_array($columnNames) ? $columnNames : array_map(trim(...), explode(',', $columnNames));
$result = (new Table($this->db, $this))
->fromTable($this->db->DBPrefix . $table)
->dropColumn($columns)
->run();
if (! $result && $this->db->DBDebug) {
throw new DatabaseException(sprintf(
'Failed to drop column%s "%s" on "%s" table.',
count($columns) > 1 ? 's' : '',
implode('", "', $columns),
$table,
));
}
return $result;
}
/**
* @param array|string $processedFields Processed column definitions
* or column names to DROP
@ -121,17 +146,6 @@ class Forge extends BaseForge
protected function _alterTable(string $alterType, string $table, $processedFields)
{
switch ($alterType) {
case 'DROP':
$columnNamesToDrop = $processedFields;
$sqlTable = new Table($this->db, $this);
$sqlTable->fromTable($table)
->dropColumn($columnNamesToDrop)
->run();
return ''; // Why empty string?
case 'CHANGE':
$fieldsToModify = [];

View File

@ -113,7 +113,7 @@ class Table
$this->keys = array_merge($this->keys, $this->formatKeys($this->db->getIndexData($table)));
// if primary key index exists twice then remove psuedo index name 'primary'.
$primaryIndexes = array_filter($this->keys, static fn ($index) => $index['type'] === 'primary');
$primaryIndexes = array_filter($this->keys, static fn ($index): bool => $index['type'] === 'primary');
if ($primaryIndexes !== [] && count($primaryIndexes) > 1 && array_key_exists('primary', $this->keys)) {
unset($this->keys['primary']);
@ -202,7 +202,7 @@ class Table
*/
public function dropPrimaryKey(): Table
{
$primaryIndexes = array_filter($this->keys, static fn ($index) => strtolower($index['type']) === 'primary');
$primaryIndexes = array_filter($this->keys, static fn ($index): bool => strtolower($index['type']) === 'primary');
foreach (array_keys($primaryIndexes) as $key) {
unset($this->keys[$key]);
@ -235,7 +235,7 @@ class Table
*/
public function addPrimaryKey(array $fields): Table
{
$primaryIndexes = array_filter($this->keys, static fn ($index) => strtolower($index['type']) === 'primary');
$primaryIndexes = array_filter($this->keys, static fn ($index): bool => strtolower($index['type']) === 'primary');
// if primary key already exists we can't add another one
if ($primaryIndexes !== []) {
@ -308,7 +308,7 @@ class Table
$this->keys = array_filter(
$this->keys,
static fn ($index) => count(array_intersect($index['fields'], $fieldNames)) === count($index['fields'])
static fn ($index): bool => count(array_intersect($index['fields'], $fieldNames)) === count($index['fields'])
);
// Unique/Index keys

View File

@ -218,7 +218,7 @@ class Database extends BaseCollector
$this->getConnections();
$queryCount = count(static::$queries);
$uniqueCount = count(array_filter(static::$queries, static fn ($query) => $query['duplicate'] === false));
$uniqueCount = count(array_filter(static::$queries, static fn ($query): bool => $query['duplicate'] === false));
$connectionCount = count($this->connections);
return sprintf(

View File

@ -181,7 +181,7 @@ class Entity implements JsonSerializable
{
$this->_cast = $cast;
$keys = array_filter(array_keys($this->attributes), static fn ($key) => ! str_starts_with($key, '_'));
$keys = array_filter(array_keys($this->attributes), static fn ($key): bool => ! str_starts_with($key, '_'));
if (is_array($this->datamap)) {
$keys = array_unique(

View File

@ -108,7 +108,7 @@ class FileCollection implements Countable, IteratorAggregate
$pattern = "#\\A{$pattern}\\z#";
}
return array_filter($files, static fn ($value) => (bool) preg_match($pattern, basename($value)));
return array_filter($files, static fn ($value): bool => (bool) preg_match($pattern, basename($value)));
}
// --------------------------------------------------------------------

View File

@ -1021,7 +1021,7 @@ class URI implements Stringable
// Encode characters
$path = preg_replace_callback(
'/(?:[^' . static::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
static fn (array $matches) => rawurlencode($matches[0]),
static fn (array $matches): string => rawurlencode($matches[0]),
$path
);
@ -1167,9 +1167,9 @@ class URI implements Stringable
$return = [];
$query = explode('&', $query);
$params = array_map(static fn (string $chunk) => preg_replace_callback(
$params = array_map(static fn (string $chunk): ?string => preg_replace_callback(
'/^(?<key>[^&=]+?)(?:\[[^&=]*\])?=(?<value>[^&=]+)/',
static fn (array $match) => str_replace($match['key'], bin2hex($match['key']), $match[0]),
static fn (array $match): string => str_replace($match['key'], bin2hex($match['key']), $match[0]),
urldecode($chunk)
), $query);

View File

@ -58,7 +58,7 @@ final class ArrayHelper
);
return array_map(
static fn ($key) => str_replace('\.', '.', $key),
static fn ($key): string => str_replace('\.', '.', $key),
$segments
);
}
@ -96,7 +96,7 @@ final class ArrayHelper
$answer[] = self::arraySearchDot($indexes, $value);
}
$answer = array_filter($answer, static fn ($value) => $value !== null);
$answer = array_filter($answer, static fn ($value): bool => $value !== null);
if ($answer !== []) {
// If array only has one element, we return that element for BC.

View File

@ -739,10 +739,10 @@ if (! function_exists('validation_show_error')) {
$config = config(Validation::class);
$view = service('renderer');
$errors = array_filter(validation_errors(), static fn ($key) => preg_match(
$errors = array_filter(validation_errors(), static fn ($key): bool => preg_match(
'/^' . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) . '$/',
$key
), ARRAY_FILTER_USE_KEY);
) === 1, ARRAY_FILTER_USE_KEY);
if ($errors === []) {
return '';

View File

@ -206,11 +206,11 @@ class Language
$argsString = implode(
', ',
array_map(static fn ($element) => '"' . $element . '"', $args)
array_map(static fn ($element): string => '"' . $element . '"', $args)
);
$argsUrlEncoded = implode(
', ',
array_map(static fn ($element) => '"' . rawurlencode($element) . '"', $args)
array_map(static fn ($element): string => '"' . rawurlencode($element) . '"', $args)
);
log_message(

View File

@ -184,7 +184,7 @@ final class AutoRouter implements AutoRouterInterface
*/
private function scanControllers(array $segments): array
{
$segments = array_filter($segments, static fn ($segment) => $segment !== '');
$segments = array_filter($segments, static fn ($segment): bool => $segment !== '');
// numerically reindex the array, removing gaps
$segments = array_values($segments);

View File

@ -136,7 +136,7 @@ final class AutoRouterImproved implements AutoRouterInterface
private function createSegments(string $uri): array
{
$segments = explode('/', $uri);
$segments = array_filter($segments, static fn ($segment) => $segment !== '');
$segments = array_filter($segments, static fn ($segment): bool => $segment !== '');
// numerically reindex the array, removing gaps
return array_values($segments);
@ -209,7 +209,7 @@ final class AutoRouterImproved implements AutoRouterInterface
}
$namespaces = array_map(
fn ($segment) => $this->translateURI($segment),
fn ($segment): string => $this->translateURI($segment),
$segments
);

View File

@ -1504,7 +1504,7 @@ class RouteCollection implements RouteCollectionInterface
for ($i = (int) $options['offset'] + 1; $i < (int) $options['offset'] + 7; $i++) {
$to = preg_replace_callback(
'/\$X/',
static fn ($m) => '$' . $i,
static fn ($m): string => '$' . $i,
$to,
1
);

View File

@ -590,7 +590,7 @@ class Router implements RouterInterface
*/
protected function scanControllers(array $segments): array
{
$segments = array_filter($segments, static fn ($segment) => $segment !== '');
$segments = array_filter($segments, static fn ($segment): bool => $segment !== '');
// numerically reindex the array, removing gaps
$segments = array_values($segments);

View File

@ -41,7 +41,7 @@ trait ReflectionHelper
$refMethod = new ReflectionMethod($obj, $method);
$obj = (gettype($obj) === 'object') ? $obj : null;
return static fn (...$args) => $refMethod->invokeArgs($obj, $args);
return static fn (...$args): mixed => $refMethod->invokeArgs($obj, $args);
}
/**

View File

@ -32,7 +32,7 @@ final class DotArrayFilter
foreach ($indexes as $index) {
$segments = preg_split('/(?<!\\\\)\./', $index, -1, PREG_SPLIT_NO_EMPTY);
$segments = array_map(static fn ($key) => str_replace('\.', '.', $key), $segments);
$segments = array_map(static fn ($key): string => str_replace('\.', '.', $key), $segments);
$filteredArray = self::filter($segments, $array);

View File

@ -176,7 +176,7 @@ class Validation implements ValidationInterface
$values = array_filter(
$flattenedArray,
static fn ($key) => preg_match(self::getRegex($field), $key),
static fn ($key): bool => preg_match(self::getRegex($field), $key) === 1,
ARRAY_FILTER_USE_KEY
);
@ -413,7 +413,7 @@ class Validation implements ValidationInterface
}
// Otherwise remove the if_exist rule and continue the process
$rules = array_filter($rules, static fn ($rule) => $rule instanceof Closure || $rule !== 'if_exist');
$rules = array_filter($rules, static fn ($rule): bool => $rule instanceof Closure || $rule !== 'if_exist');
}
return $rules;
@ -460,7 +460,7 @@ class Validation implements ValidationInterface
}
}
$rules = array_filter($rules, static fn ($rule) => $rule instanceof Closure || $rule !== 'permit_empty');
$rules = array_filter($rules, static fn ($rule): bool => $rule instanceof Closure || $rule !== 'permit_empty');
}
return $rules;
@ -867,7 +867,7 @@ class Validation implements ValidationInterface
$errors = array_filter(
$this->getErrors(),
static fn ($key) => preg_match(self::getRegex($field), $key),
static fn ($key): bool => preg_match(self::getRegex($field), $key) === 1,
ARRAY_FILTER_USE_KEY
);

View File

@ -228,13 +228,13 @@ class Table
$missingKeys = array_diff_key($keyIndex, $tmpRow);
// Remove all keys which don't exist in $keyIndex
$tmpRow = array_filter($tmpRow, static fn ($k) => array_key_exists($k, $keyIndex), ARRAY_FILTER_USE_KEY);
$tmpRow = array_filter($tmpRow, static fn ($k): bool => array_key_exists($k, $keyIndex), ARRAY_FILTER_USE_KEY);
// add missing keys to row, but use $this->emptyCells
$tmpRow = array_merge($tmpRow, array_map(fn ($v) => ['data' => $this->emptyCells], $missingKeys));
$tmpRow = array_merge($tmpRow, array_map(fn ($v): array => ['data' => $this->emptyCells], $missingKeys));
// order keys by $keyIndex values
uksort($tmpRow, static fn ($k1, $k2) => $keyIndex[$k1] <=> $keyIndex[$k2]);
uksort($tmpRow, static fn ($k1, $k2): int => $keyIndex[$k1] <=> $keyIndex[$k2]);
}
$this->rows[] = $tmpRow;

View File

@ -15,7 +15,7 @@ namespace Tests\Support\Config;
// This is a simple file to include for testing the RouteCollection class.
$routes->add('testing', 'TestController::index', ['as' => 'testing-index']);
$routes->get('closure', static fn () => 'closure test');
$routes->get('closure', static fn (): string => 'closure test');
$routes->get('/', 'Blog::index', ['hostname' => 'blog.example.com']);
$routes->get('/', 'Sub::index', ['subdomain' => 'sub']);
$routes->get('/all', 'AllDomain::index', ['subdomain' => '*']);

View File

@ -136,7 +136,7 @@ final class FrameworkCodeTest extends TestCase
$testClasses = array_filter(
$testClasses,
static fn (string $class) => is_subclass_of($class, TestCase::class)
static fn (string $class): bool => is_subclass_of($class, TestCase::class)
);
sort($testClasses);

View File

@ -42,7 +42,7 @@ final class DummyHandlerTest extends CIUnitTestCase
public function testRemember(): void
{
$dummyHandler = $this->handler->remember('key', 2, static fn () => 'value');
$dummyHandler = $this->handler->remember('key', 2, static fn (): string => 'value');
$this->assertNull($dummyHandler);
}

View File

@ -135,7 +135,7 @@ final class FileHandlerTest extends AbstractHandlerTestCase
*/
public function testRemember(): void
{
$this->handler->remember(self::$key1, 2, static fn () => 'value');
$this->handler->remember(self::$key1, 2, static fn (): string => 'value');
$this->assertSame('value', $this->handler->get(self::$key1));
$this->assertNull($this->handler->get(self::$dummy));

View File

@ -86,7 +86,7 @@ final class MemcachedHandlerTest extends AbstractHandlerTestCase
*/
public function testRemember(): void
{
$this->handler->remember(self::$key1, 2, static fn () => 'value');
$this->handler->remember(self::$key1, 2, static fn (): string => 'value');
$this->assertSame('value', $this->handler->get(self::$key1));
$this->assertNull($this->handler->get(self::$dummy));

View File

@ -91,7 +91,7 @@ final class PredisHandlerTest extends AbstractHandlerTestCase
*/
public function testRemember(): void
{
$this->handler->remember(self::$key1, 2, static fn () => 'value');
$this->handler->remember(self::$key1, 2, static fn (): string => 'value');
$this->assertSame('value', $this->handler->get(self::$key1));
$this->assertNull($this->handler->get(self::$dummy));

View File

@ -97,7 +97,7 @@ final class RedisHandlerTest extends AbstractHandlerTestCase
*/
public function testRemember(): void
{
$this->handler->remember(self::$key1, 2, static fn () => 'value');
$this->handler->remember(self::$key1, 2, static fn (): string => 'value');
$this->assertSame('value', $this->handler->get(self::$key1));
$this->assertNull($this->handler->get(self::$dummy));

View File

@ -214,7 +214,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$routes = service('routes');
$routes->add(
'pages/(:segment)',
static fn ($segment) => 'You want to see "' . esc($segment) . '" page.'
static fn ($segment): string => 'You want to see "' . esc($segment) . '" page.'
);
$router = service('router', $routes, service('incomingrequest'));
Services::injectMock('router', $router);

View File

@ -13,7 +13,9 @@ declare(strict_types=1);
namespace CodeIgniter;
use CodeIgniter\Autoloader\Autoloader;
use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Files\Exceptions\FileNotFoundException;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
use PHPUnit\Framework\Attributes\CoversFunction;
@ -148,4 +150,33 @@ final class CommonHelperTest extends CIUnitTestCase
$this->assertSame($this->dummyHelpers[0], foo_bar_baz());
}
public function testNamespacedHelperNotFound(): void
{
$this->expectException(FileNotFoundException::class);
$locator = $this->getMockLocator();
Services::injectMock('locator', $locator);
helper('foo\barbaz');
}
public function testNamespacedHelperFound(): void
{
$autoloader = new Autoloader();
$autoloader->addNamespace('Tests\Support\Helpers', TESTPATH . '_support/Helpers');
$locator = new FileLocator($autoloader);
Services::injectMock('locator', $locator);
$found = true;
try {
helper('Tests\Support\Helpers\baguette');
} catch (FileNotFoundException) {
$found = false;
}
$this->assertTrue($found);
}
}

View File

@ -47,7 +47,7 @@ final class UnionTest extends CIUnitTestCase
$builder = $this->db->table('test');
$builder->union(static fn ($builder) => $builder->from('test'));
$builder->union(static fn ($builder): BaseBuilder => $builder->from('test'));
$this->assertSame($expected, $this->buildSelect($builder));
}
@ -85,7 +85,7 @@ final class UnionTest extends CIUnitTestCase
$builder = $db->table('users');
$builder->union(static fn ($builder) => $builder->from('users'));
$builder->union(static fn ($builder): BaseBuilder => $builder->from('users'));
$this->assertSame($expected, $this->buildSelect($builder));
}

View File

@ -1319,8 +1319,7 @@ final class ForgeTest extends CIUnitTestCase
$this->forge->createTable('forge_test_two');
$this->assertTrue($this->db->fieldExists('name', 'forge_test_two'));
$this->forge->dropColumn('forge_test_two', 'name');
$this->assertTrue($this->forge->dropColumn('forge_test_two', 'name'));
$this->db->resetDataCache();
@ -1707,7 +1706,7 @@ final class ForgeTest extends CIUnitTestCase
// check that db_actions_name key exists
$indexes = array_filter(
$allIndexes,
static fn ($index) => ($index->name === 'db_actions_name')
static fn ($index): bool => ($index->name === 'db_actions_name')
&& ($index->fields === [0 => 'name'])
);
$this->assertCount(1, $indexes);
@ -1715,7 +1714,7 @@ final class ForgeTest extends CIUnitTestCase
// check that db_actions_category_name key exists
$indexes = array_filter(
$allIndexes,
static fn ($index) => ($index->name === 'db_actions_category_name')
static fn ($index): bool => ($index->name === 'db_actions_category_name')
&& ($index->fields === [0 => 'category', 1 => 'name'])
);
$this->assertCount(1, $indexes);
@ -1723,7 +1722,7 @@ final class ForgeTest extends CIUnitTestCase
// check that the primary key exists
$indexes = array_filter(
$allIndexes,
static fn ($index) => $index->type === 'PRIMARY'
static fn ($index): bool => $index->type === 'PRIMARY'
);
$this->assertCount(1, $indexes);
@ -1755,7 +1754,7 @@ final class ForgeTest extends CIUnitTestCase
// check that db_actions_name key exists
$indexes = array_filter(
$allIndexes,
static fn ($index) => ($index->name === 'db_actions_name')
static fn ($index): bool => ($index->name === 'db_actions_name')
&& ($index->fields === [0 => 'name'])
);
$this->assertCount(1, $indexes);
@ -1781,7 +1780,7 @@ final class ForgeTest extends CIUnitTestCase
// check that the primary key exists
$indexes = array_filter(
$allIndexes,
static fn ($index) => $index->type === 'PRIMARY'
static fn ($index): bool => $index->type === 'PRIMARY'
);
$this->assertCount(1, $indexes);

View File

@ -157,7 +157,7 @@ final class TimerTest extends CIUnitTestCase
public function testRecordArrowFunction(): void
{
$timer = new Timer();
$returnValue = $timer->record('longjohn', static fn () => strlen('CI4'));
$returnValue = $timer->record('longjohn', static fn (): int => strlen('CI4'));
$this->assertLessThan(0.1, $timer->getElapsedTime('longjohn'));
$this->assertSame(3, $returnValue);
@ -196,7 +196,7 @@ final class TimerTest extends CIUnitTestCase
public function testCommonNoNameCallableExpectTimer(): void
{
$returnValue = timer(null, static fn () => strlen('CI4'));
$returnValue = timer(null, static fn (): int => strlen('CI4'));
$this->assertInstanceOf(Timer::class, $returnValue);
}
@ -212,7 +212,7 @@ final class TimerTest extends CIUnitTestCase
public function testCommonCallableExpectWithReturn(): void
{
$returnValue = timer('common', static fn () => strlen('CI4'));
$returnValue = timer('common', static fn (): int => strlen('CI4'));
$this->assertNotInstanceOf(Timer::class, $returnValue);
$this->assertSame(3, $returnValue);

View File

@ -441,7 +441,7 @@ final class ContentSecurityPolicyTest extends CIUnitTestCase
$result = $this->work($body);
$nonceStyle = array_filter(
$this->getPrivateProperty($this->csp, 'styleSrc'),
static fn ($value) => str_starts_with($value, 'nonce-')
static fn ($value): bool => str_starts_with($value, 'nonce-')
);
$this->assertStringContainsString('nonce=', $this->response->getBody());
@ -516,7 +516,7 @@ final class ContentSecurityPolicyTest extends CIUnitTestCase
$result = $this->work($body);
$nonceScript = array_filter(
$this->getPrivateProperty($this->csp, 'scriptSrc'),
static fn ($value) => str_starts_with($value, 'nonce-')
static fn ($value): bool => str_starts_with($value, 'nonce-')
);
$this->assertStringContainsString('nonce=', $this->response->getBody());

View File

@ -51,8 +51,8 @@ final class DefinedRouteCollectorTest extends CIUnitTestCase
$routes->get('journals', 'Blogs');
$routes->get('100', 'Home::index');
$routes->get('product/(:num)', 'Catalog::productLookupByID/$1');
$routes->get('feed', static fn () => 'A Closure route.');
$routes->get('200', static fn () => 'A Closure route.');
$routes->get('feed', static fn (): string => 'A Closure route.');
$routes->get('200', static fn (): string => 'A Closure route.');
$routes->view('about', 'pages/about');
$collector = new DefinedRouteCollector($routes);

View File

@ -68,8 +68,8 @@ final class RouterTest extends CIUnitTestCase
'posts/(:num)' => 'Blog::show/$1',
'posts/(:num)/edit' => 'Blog::edit/$1',
'books/(:num)/(:alpha)/(:num)' => 'Blog::show/$3/$1',
'closure/(:num)/(:alpha)' => static fn ($num, $str) => $num . '-' . $str,
'closure-dash/(:num)/(:alpha)' => static fn ($num, $str) => $num . '-' . $str,
'closure/(:num)/(:alpha)' => static fn ($num, $str): string => $num . '-' . $str,
'closure-dash/(:num)/(:alpha)' => static fn ($num, $str): string => $num . '-' . $str,
'{locale}/pages' => 'App\Pages::list_all',
'test/(:any)/lang/{locale}' => 'App\Pages::list_all',
'admin/admins' => 'App\Admin\Admins::list_all',

View File

@ -54,7 +54,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'GET',
'home',
static fn () => 'Hello World',
static fn (): string => 'Hello World',
],
]);
$response = $this->get('home');
@ -74,7 +74,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'GET',
'foo/bar/1/2/3',
static fn () => 'Hello World',
static fn (): string => 'Hello World',
],
]);
$response = $this->get('foo/bar/1/2/3');
@ -90,7 +90,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'GET',
'admin',
static fn () => 'Admin Area',
static fn (): string => 'Admin Area',
['filter' => 'test-redirectfilter'],
],
]);
@ -123,7 +123,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'POST',
'home',
static fn () => 'Hello Mars',
static fn (): string => 'Hello Mars',
],
]);
$response = $this->post('home');
@ -137,7 +137,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'POST',
'home',
static fn () => 'Hello ' . service('request')->getPost('foo') . '!',
static fn (): string => 'Hello ' . service('request')->getPost('foo') . '!',
],
]);
$response = $this->post('home', ['foo' => 'Mars']);
@ -181,7 +181,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'PUT',
'home',
static fn () => 'Hello Pluto',
static fn (): string => 'Hello Pluto',
],
]);
$response = $this->put('home');
@ -195,7 +195,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'PATCH',
'home',
static fn () => 'Hello Jupiter',
static fn (): string => 'Hello Jupiter',
],
]);
$response = $this->patch('home');
@ -209,7 +209,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'OPTIONS',
'home',
static fn () => 'Hello George',
static fn (): string => 'Hello George',
],
]);
$response = $this->options('home');
@ -223,7 +223,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'DELETE',
'home',
static fn () => 'Hello Wonka',
static fn (): string => 'Hello Wonka',
],
]);
$response = $this->delete('home');
@ -237,7 +237,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'GET',
'home',
static fn () => 'Home',
static fn (): string => 'Home',
],
])->withSession([
'fruit' => 'apple',
@ -259,7 +259,7 @@ final class FeatureTestTraitTest extends CIUnitTestCase
[
'GET',
'home',
static fn () => 'Home',
static fn (): string => 'Home',
],
])->withSession()->get('home');

View File

@ -119,7 +119,7 @@ class RulesTest extends CIUnitTestCase
],
// Testing with closure
[
['foo' => ['if_exist', static fn ($value) => true]],
['foo' => ['if_exist', static fn ($value): bool => true]],
['foo' => []],
true,
],
@ -299,7 +299,7 @@ class RulesTest extends CIUnitTestCase
],
[
// Testing with closure
['foo' => ['permit_empty', static fn ($value) => true]],
['foo' => ['permit_empty', static fn ($value): bool => true]],
['foo' => ''],
true,
],

View File

@ -91,7 +91,7 @@ final class RulesTest extends TraditionalRulesTest
],
// Testing with closure
[
['foo' => ['permit_empty', static fn ($value) => true]],
['foo' => ['permit_empty', static fn ($value): bool => true]],
['foo' => ''],
true,
],

View File

@ -284,7 +284,7 @@ class ValidationTest extends CIUnitTestCase
{
$this->validation->setRules(
[
'foo' => ['required', static fn ($value) => $value === 'abc'],
'foo' => ['required', static fn ($value): bool => $value === 'abc'],
],
[
// Errors
@ -339,7 +339,7 @@ class ValidationTest extends CIUnitTestCase
$this->validation->setRules([
'secret' => [
'label' => 'シークレット',
'rules' => ['required', static fn ($value) => $value === 'abc'],
'rules' => ['required', static fn ($value): bool => $value === 'abc'],
'errors' => [
// Specify the array key for the closure rule.
1 => 'The {field} is invalid',

View File

@ -797,7 +797,7 @@ final class ParserTest extends CIUnitTestCase
public function testParserPluginClosure(): void
{
$config = $this->config;
$config->plugins['hello'] = static fn (array $params = []) => 'Hello, ' . trim($params[0]);
$config->plugins['hello'] = static fn (array $params = []): string => 'Hello, ' . trim($params[0]);
$this->parser = new Parser($config, $this->viewsDir, $this->loader);
@ -828,7 +828,7 @@ final class ParserTest extends CIUnitTestCase
public function testParserSingleTag(): void
{
$this->parser->addPlugin('hit:it', static fn () => 'Hip to the Hop', false);
$this->parser->addPlugin('hit:it', static fn (): string => 'Hip to the Hop', false);
$template = '{+ hit:it +}';
@ -837,7 +837,7 @@ final class ParserTest extends CIUnitTestCase
public function testParserSingleTagWithParams(): void
{
$this->parser->addPlugin('hit:it', static fn (array $params = []) => "{$params['first']} to the {$params['last']}", false);
$this->parser->addPlugin('hit:it', static fn (array $params = []): string => "{$params['first']} to the {$params['last']}", false);
$template = '{+ hit:it first=foo last=bar +}';
@ -846,7 +846,7 @@ final class ParserTest extends CIUnitTestCase
public function testParserSingleTagWithSingleParams(): void
{
$this->parser->addPlugin('hit:it', static fn (array $params = []) => "{$params[0]} to the {$params[1]}", false);
$this->parser->addPlugin('hit:it', static fn (array $params = []): string => "{$params[0]} to the {$params[1]}", false);
$template = '{+ hit:it foo bar +}';

View File

@ -12,6 +12,7 @@ See all the changes.
.. toctree::
:titlesonly:
v4.5.7
v4.5.6
v4.5.5
v4.5.4

View File

@ -0,0 +1,22 @@
#############
Version 4.5.7
#############
Release Date: December 31, 2024
**4.5.7 release of CodeIgniter4**
.. contents::
:local:
:depth: 3
**********
Bugs Fixed
**********
- **Common:** Fixed a bug where the ``helper()`` method may throw `FileNotFoundException` on valid namespaced helper.
- **Forge:** Fixed an issue where `SQLite3`'s Forge always returns `false` when calling ``dropColumn()``.
See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
for a complete list of bugs fixed.

View File

@ -26,7 +26,7 @@ copyright = '2019-' + str(year_now) + ' CodeIgniter Foundation'
version = '4.5'
# The full version, including alpha/beta/rc tags.
release = '4.5.6'
release = '4.5.7'
# -- General configuration ---------------------------------------------------

View File

@ -1,9 +1,7 @@
<?php
$pQuery = $db->prepare(static function ($db) {
return $db->table('user')->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US',
]);
});
$pQuery = $db->prepare(static fn ($db) => $db->table('user')->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US',
]));

View File

@ -1,13 +1,11 @@
<?php
// Prepare the Query
$pQuery = $db->prepare(static function ($db) {
return $db->table('user')->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US',
]);
});
$pQuery = $db->prepare(static fn ($db) => $db->table('user')->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US',
]));
// Collect the Data
$name = 'John Doe';

View File

@ -0,0 +1,32 @@
#############################
Upgrading from 4.5.6 to 4.5.7
#############################
Please refer to the upgrade instructions corresponding to your installation method.
- :ref:`Composer Installation App Starter Upgrading <app-starter-upgrading>`
- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading <adding-codeigniter4-upgrading>`
- :ref:`Manual Installation Upgrading <installing-manual-upgrading>`
.. contents::
:local:
:depth: 2
*************
Project Files
*************
Some files in the **project space** (root, app, public, writable) received updates. Due to
these files being outside of the **system** scope they will not be changed without your intervention.
.. note:: There are some third-party CodeIgniter modules available to assist
with merging changes to the project space:
`Explore on Packagist <https://packagist.org/explore/?query=codeigniter4%20updates>`_.
All Changes
===========
This is a list of all files in the **project space** that received changes;
many will be simple comments or formatting that have no effect on the runtime:
- app/Views/errors/cli/error_exception.php

View File

@ -16,6 +16,7 @@ See also :doc:`./backward_compatibility_notes`.
backward_compatibility_notes
upgrade_457
upgrade_456
upgrade_455
upgrade_454

View File

@ -1,4 +1,4 @@
# total 1686 errors
# total 1685 errors
parameters:
ignoreErrors:
@ -1697,11 +1697,6 @@ parameters:
count: 1
path: ../../system/Database/Forge.php
-
message: '#^Method CodeIgniter\\Database\\Forge\:\:dropColumn\(\) has parameter \$columnNames with no value type specified in iterable type array\.$#'
count: 1
path: ../../system/Database/Forge.php
-
message: '#^Method CodeIgniter\\Database\\Forge\:\:modifyColumn\(\) has parameter \$fields with no value type specified in iterable type array\.$#'
count: 1