diff --git a/.github/workflows/label-add-conflict-all-pr.yml b/.github/workflows/label-add-conflict-all-pr.yml
index 5147d678f86c..e83e4daf0b99 100644
--- a/.github/workflows/label-add-conflict-all-pr.yml
+++ b/.github/workflows/label-add-conflict-all-pr.yml
@@ -46,6 +46,6 @@ jobs:
gh pr edit $url --add-label "stale"
# Add a comment
- gh pr comment $url --body ":wave: Hi, @$author! We detected conflicts in your PR against the base branch :speak_no_evil: You may want to sync :arrows_counterclockwise: your branch with upstream! Ref: [Syncing Your Branch](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/workflow.md#pushing-your-branch)"
+ gh pr comment $url --body ":wave: Hi, @$author! We detected conflicts in your PR against the base branch :speak_no_evil: You may want to sync :arrows_counterclockwise: your branch with upstream! Ref: [Syncing Your Branch](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/workflow.md#updating-your-branch)"
fi
done
diff --git a/.github/workflows/test-phpcpd.yml b/.github/workflows/test-phpcpd.yml
index 61093d449a93..1de4c8167b9a 100644
--- a/.github/workflows/test-phpcpd.yml
+++ b/.github/workflows/test-phpcpd.yml
@@ -12,7 +12,6 @@ on:
- 'public/**.php'
- 'system/**.php'
- '.github/workflows/test-phpcpd.yml'
-
push:
branches:
- 'develop'
@@ -23,40 +22,21 @@ on:
- 'system/**.php'
- '.github/workflows/test-phpcpd.yml'
-concurrency:
- group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
- cancel-in-progress: true
-
-permissions:
- contents: read
-
jobs:
- build:
- name: Duplicate Code Detection
- runs-on: ubuntu-22.04
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: '8.1'
- tools: phpcpd
- extensions: dom, mbstring
-
- - name: Detect code duplication
- run: phpcpd
- --exclude system/Test
- --exclude system/ThirdParty
- --exclude system/Database/SQLSRV/Builder.php
- --exclude system/Database/SQLSRV/Forge.php
- --exclude system/Database/MySQLi/Builder.php
- --exclude system/Database/OCI8/Builder.php
- --exclude system/Database/Postgre/Builder.php
- --exclude system/Debug/Exceptions.php
- --exclude system/HTTP/SiteURI.php
- --exclude system/Validation/Rules.php
- --exclude system/Autoloader/Autoloader.php
- --exclude system/Config/Filters.php
- -- app/ public/ system/
+ phpcpd:
+ uses: codeigniter4/.github/.github/workflows/phpcpd.yml@main
+ with:
+ dirs: "app/ public/ system/"
+ options: >-
+ --exclude system/Test
+ --exclude system/ThirdParty
+ --exclude system/Database/SQLSRV/Builder.php
+ --exclude system/Database/SQLSRV/Forge.php
+ --exclude system/Database/MySQLi/Builder.php
+ --exclude system/Database/OCI8/Builder.php
+ --exclude system/Database/Postgre/Builder.php
+ --exclude system/Debug/Exceptions.php
+ --exclude system/HTTP/SiteURI.php
+ --exclude system/Validation/Rules.php
+ --exclude system/Autoloader/Autoloader.php
+ --exclude system/Config/Filters.php
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d2778139204..3d9867ef29c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
# Changelog
-## [v4.5.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.5.3) (2024-07-27)
+## [v4.5.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.5.5) (2024-09-07)
+[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.5.4...v4.5.5)
+
+### Fixed Bugs
+
+* fix: Validation rule `differs`/`matches` with dot array by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9103
+* fix: update preload.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9111
+* fix: [Validation] TypeError when using numeric field names by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9142
+* fix: `auto_link()` regexp by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9169
+
+### Refactoring
+
+* refactor: reduce_multiples() and fix user guide by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9099
+* refactor: enable AddMethodCallBasedStrictParamTypeRector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/9156
+* refactor: BaseBuilder by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9157
+* refactor: improve error message for missing PHP DB extensions by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/9160
+* refactor: fix typo in BaseConnection.php by @ThomasMeschke in https://github.com/codeigniter4/CodeIgniter4/pull/9170
+
+## [v4.5.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.5.4) (2024-07-27)
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.5.3...v4.5.4)
### Fixed Bugs
diff --git a/admin/framework/composer.json b/admin/framework/composer.json
index d46c7490ec56..79fc5c282e50 100644
--- a/admin/framework/composer.json
+++ b/admin/framework/composer.json
@@ -23,7 +23,7 @@
"kint-php/kint": "^5.0.4",
"mikey179/vfsstream": "^1.6",
"nexusphp/cs-config": "^3.6",
- "phpunit/phpunit": "^10.5.16",
+ "phpunit/phpunit": "^10.5.16 || ^11.2",
"predis/predis": "^1.1 || ^2.0"
},
"suggest": {
diff --git a/composer.json b/composer.json
index 6915af74aa8f..c2ed6374b550 100644
--- a/composer.json
+++ b/composer.json
@@ -25,10 +25,10 @@
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-strict-rules": "^1.6",
- "phpunit/phpcov": "^9.0.2",
- "phpunit/phpunit": "^10.5.16",
+ "phpunit/phpcov": "^9.0.2 || ^10.0",
+ "phpunit/phpunit": "^10.5.16 || ^11.2",
"predis/predis": "^1.1 || ^2.0",
- "rector/rector": "1.2.2"
+ "rector/rector": "1.2.4"
},
"replace": {
"codeigniter4/framework": "self.version"
diff --git a/contributing/internals.md b/contributing/internals.md
index b49c9832d390..124d19209ac8 100644
--- a/contributing/internals.md
+++ b/contributing/internals.md
@@ -13,17 +13,17 @@ projects outside of CodeIgniter. Basically, this means that any
dependencies should be kept to a minimum. Any dependencies must be able
to be passed into the constructor. If you do need to use one of the
other core packages, you can create that in the constructor using the
-`Services` class, as long as you provide a way for dependencies to
+`service()` function, as long as you provide a way for dependencies to
override that:
```php
public function __construct(?Foo $foo = null)
{
- $this->foo = $foo ?? \Config\Services::foo();
+ $this->foo = $foo ?? service('foo');
}
```
-## Type declarations
+## Type Declarations
PHP7 provides [Type declarations](https://www.php.net/manual/en/language.types.declarations.php)
for method parameters and return types. Use it where possible. Return type
@@ -112,10 +112,9 @@ should generally match the package name.
## Autoloader
-All files within the package should be added to
-**system/Config/AutoloadConfig.php**, in the "classmap" property. This
-is only used for core framework files, and helps to minimize file system
-scans and keep performance high.
+All source files within the **system/ThirdParty** should be added to
+**system/Config/AutoloadConfig.php**, in the `$coreClassmap` property. This
+is only used for loading the third party packages without Composer.
## Command-Line Support
diff --git a/contributing/pull_request.md b/contributing/pull_request.md
index 05e060f797ec..8e3b5ed9f419 100644
--- a/contributing/pull_request.md
+++ b/contributing/pull_request.md
@@ -260,7 +260,7 @@ PHPStan is expected to scan the entire framework by running this command in your
terminal:
```console
-vendor/bin/phpstan analyse
+composer phpstan:check
```
See also:
@@ -272,7 +272,7 @@ false positive and should be ignored, the baseline can be updated with the follo
command:
```console
-vendor/bin/phpstan analyze --generate-baseline phpstan-baseline.php
+composer phpstan:baseline
```
#### Rector
diff --git a/contributing/workflow.md b/contributing/workflow.md
index 30dfc80492af..a294cc9e485c 100644
--- a/contributing/workflow.md
+++ b/contributing/workflow.md
@@ -354,6 +354,13 @@ You might get conflicts when you rebase. It is your
responsibility to resolve those locally, so that you can continue
collaborating with the shared repository.
+Occasionally, the Composer packages for development may be updated. Run the
+following command to use the latest packages:
+
+```console
+composer update
+```
+
And finally push your local branch to your GitHub repository:
```console
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
index e67b1b29ed55..7f13b0e78cff 100644
--- a/phpdoc.dist.xml
+++ b/phpdoc.dist.xml
@@ -10,7 +10,7 @@
api/build/
api/cache/
-
+
system
diff --git a/phpstan-baseline.php b/phpstan-baseline.php
index 1b36c4a8f8ee..1ba751d7c11d 100644
--- a/phpstan-baseline.php
+++ b/phpstan-baseline.php
@@ -103,12 +103,6 @@
'count' => 1,
'path' => __DIR__ . '/system/BaseModel.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Method CodeIgniter\\\\BaseModel\\:\\:doErrors\\(\\) return type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/system/BaseModel.php',
-];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Method CodeIgniter\\\\BaseModel\\:\\:doFind\\(\\) has parameter \\$id with no value type specified in iterable type array\\.$#',
@@ -1492,7 +1486,7 @@
$ignoreErrors[] = [
// identifier: empty.notAllowed
'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#',
- 'count' => 30,
+ 'count' => 29,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
$ignoreErrors[] = [
@@ -1939,6 +1933,12 @@
'count' => 1,
'path' => __DIR__ . '/system/Database/BaseBuilder.php',
];
+$ignoreErrors[] = [
+ // identifier: isset.offset
+ 'message' => '#^Offset 4 on array\\{string, string, string, string, string, string\\} in isset\\(\\) always exists and is not nullable\\.$#',
+ 'count' => 1,
+ 'path' => __DIR__ . '/system/Database/BaseBuilder.php',
+];
$ignoreErrors[] = [
// identifier: booleanNot.exprNotBoolean
'message' => '#^Only booleans are allowed in a negated boolean, TWhenNot given\\.$#',
@@ -5149,18 +5149,6 @@
'count' => 1,
'path' => __DIR__ . '/system/Entity/Entity.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property CodeIgniter\\\\Entity\\\\Entity\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/system/Entity/Entity.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property CodeIgniter\\\\Entity\\\\Entity\\:\\:\\$original type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/system/Entity/Entity.php',
-];
$ignoreErrors[] = [
// identifier: codeigniter.configArgumentInstanceof
'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#',
@@ -5935,12 +5923,6 @@
'count' => 1,
'path' => __DIR__ . '/system/HTTP/ContentSecurityPolicy.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property CodeIgniter\\\\HTTP\\\\ContentSecurityPolicy\\:\\:\\$validSources type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/system/HTTP/ContentSecurityPolicy.php',
-];
$ignoreErrors[] = [
// identifier: codeigniter.superglobalAccess
'message' => '#^Accessing offset \'HTTP_USER_AGENT\' directly on \\$_SERVER is discouraged\\.$#',
@@ -10483,12 +10465,6 @@
'count' => 1,
'path' => __DIR__ . '/system/Validation/Validation.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property CodeIgniter\\\\Validation\\\\Validation\\:\\:\\$rules type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/system/Validation/Validation.php',
-];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Property CodeIgniter\\\\Validation\\\\Validation\\:\\:\\$validated type has no value type specified in iterable type array\\.$#',
@@ -10999,12 +10975,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/_support/Entity/CustomUser.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property Tests\\\\Support\\\\Entity\\\\User\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/_support/Entity/User.php',
-];
$ignoreErrors[] = [
// identifier: missingType.return
'message' => '#^Method Tests\\\\Support\\\\Language\\\\SecondMockLanguage\\:\\:loaded\\(\\) has no return type specified\\.$#',
@@ -11239,12 +11209,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/_support/Models/UserModel.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property Tests\\\\Support\\\\SomeEntity\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/_support/SomeEntity.php',
-];
$ignoreErrors[] = [
// identifier: missingType.return
'message' => '#^Method Tests\\\\Support\\\\Test\\\\TestForReflectionHelper\\:\\:getPrivate\\(\\) has no return type specified\\.$#',
@@ -11487,7 +11451,7 @@
];
$ignoreErrors[] = [
// identifier: codeigniter.superglobalAccessAssign
- 'message' => '#^Assigning mixed directly on offset \'CONTENT_TYPE\' of \\$_SERVER is discouraged\\.$#',
+ 'message' => '#^Assigning string directly on offset \'CONTENT_TYPE\' of \\$_SERVER is discouraged\\.$#',
'count' => 1,
'path' => __DIR__ . '/tests/system/API/ResponseTraitTest.php',
];
@@ -11521,18 +11485,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/API/ResponseTraitTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method CodeIgniter\\\\API\\\\ResponseTraitTest\\:\\:tryValidContentType\\(\\) has parameter \\$contentType with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/API/ResponseTraitTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method CodeIgniter\\\\API\\\\ResponseTraitTest\\:\\:tryValidContentType\\(\\) has parameter \\$mimeType with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/API/ResponseTraitTest.php',
-];
$ignoreErrors[] = [
// identifier: missingType.parameter
'message' => '#^Method class@anonymous/tests/system/API/ResponseTraitTest\\.php\\:116\\:\\:__construct\\(\\) has parameter \\$formatter with no type specified\\.$#',
@@ -12967,24 +12919,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/ControllerTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/ControllerTest\\.php\\:128\\:\\:\\$signup has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/ControllerTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/ControllerTest\\.php\\:128\\:\\:\\$signup_errors has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/ControllerTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/ControllerTest\\.php\\:151\\:\\:\\$signup has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/ControllerTest.php',
-];
$ignoreErrors[] = [
// identifier: argument.type
'message' => '#^Parameter \\#1 \\$cookies of class CodeIgniter\\\\Cookie\\\\CookieStore constructor expects array\\, array\\ given\\.$#',
@@ -13747,180 +13681,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:getBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:getFakeBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:setBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:setBar\\(\\) has parameter \\$value with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:_getBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:_setBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:_setBar\\(\\) has parameter \\$value with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:getBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:setBar\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:setBar\\(\\) has parameter \\$value with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.return
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1162\\:\\:getSimple\\(\\) has no return type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method class@anonymous/tests/system/Entity/EntityTest\\.php\\:1229\\:\\:setSeventh\\(\\) has parameter \\$seventh with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1078\\:\\:\\$original type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1116\\:\\:\\$original type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1162\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1162\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1192\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1192\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1211\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1211\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1229\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1229\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1287\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1287\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.property
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1316\\:\\:\\$_original has no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:1316\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/Entity/EntityTest\\.php\\:895\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php',
-];
$ignoreErrors[] = [
// identifier: codeigniter.configArgumentInstanceof
'message' => '#^Argument \\#1 \\$name \\(\'Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#',
@@ -15955,30 +15715,6 @@
'count' => 2,
'path' => __DIR__ . '/tests/system/Images/ImageMagickHandlerTest.php',
];
-$ignoreErrors[] = [
- // identifier: method.notFound
- 'message' => '#^Call to an undefined method CodeIgniter\\\\Language\\\\Language\\:\\:disableIntlSupport\\(\\)\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Language/LanguageTest.php',
-];
-$ignoreErrors[] = [
- // identifier: method.notFound
- 'message' => '#^Call to an undefined method CodeIgniter\\\\Language\\\\Language\\:\\:loaded\\(\\)\\.$#',
- 'count' => 3,
- 'path' => __DIR__ . '/tests/system/Language/LanguageTest.php',
-];
-$ignoreErrors[] = [
- // identifier: method.notFound
- 'message' => '#^Call to an undefined method CodeIgniter\\\\Language\\\\Language\\:\\:loadem\\(\\)\\.$#',
- 'count' => 2,
- 'path' => __DIR__ . '/tests/system/Language/LanguageTest.php',
-];
-$ignoreErrors[] = [
- // identifier: method.notFound
- 'message' => '#^Call to an undefined method CodeIgniter\\\\Language\\\\Language\\:\\:setData\\(\\)\\.$#',
- 'count' => 9,
- 'path' => __DIR__ . '/tests/system/Language/LanguageTest.php',
-];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Method CodeIgniter\\\\Language\\\\LanguageTest\\:\\:provideBundleUniqueKeys\\(\\) return type has no value type specified in iterable type iterable\\.$#',
@@ -17017,12 +16753,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/RESTful/ResourcePresenterTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method CodeIgniter\\\\Router\\\\AutoRouterImprovedTest\\:\\:createNewAutoRouter\\(\\) has parameter \\$namespace with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Router/AutoRouterImprovedTest.php',
-];
$ignoreErrors[] = [
// identifier: missingType.iterableValue
'message' => '#^Method CodeIgniter\\\\Router\\\\AutoRouterImprovedTest\\:\\:provideRejectTranslateUriToCamelCase\\(\\) return type has no value type specified in iterable type iterable\\.$#',
@@ -17443,12 +17173,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/Test/BootstrapFCPATHTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.parameter
- 'message' => '#^Method CodeIgniter\\\\Test\\\\BootstrapFCPATHTest\\:\\:readOutput\\(\\) has parameter \\$file with no type specified\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/Test/BootstrapFCPATHTest.php',
-];
$ignoreErrors[] = [
// identifier: method.notFound
'message' => '#^Call to an undefined method CodeIgniter\\\\Test\\\\TestResponse\\:\\:ohno\\(\\)\\.$#',
@@ -18691,12 +18415,6 @@
'count' => 1,
'path' => __DIR__ . '/tests/system/View/ParserTest.php',
];
-$ignoreErrors[] = [
- // identifier: missingType.iterableValue
- 'message' => '#^Property class@anonymous/tests/system/View/ParserTest\\.php\\:370\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/system/View/ParserTest.php',
-];
$ignoreErrors[] = [
// identifier: method.notFound
'message' => '#^Call to an undefined method CodeIgniter\\\\View\\\\Table\\:\\:compileTemplate\\(\\)\\.$#',
diff --git a/preload.php b/preload.php
index 75d86f5cf572..755df5e4a4ed 100644
--- a/preload.php
+++ b/preload.php
@@ -38,23 +38,25 @@ class preload
[
'include' => __DIR__ . '/vendor/codeigniter4/framework/system', // Change this path if using manual installation
'exclude' => [
- '/system/bootstrap.php',
// Not needed if you don't use them.
'/system/Database/OCI8/',
'/system/Database/Postgre/',
'/system/Database/SQLite3/',
'/system/Database/SQLSRV/',
- // Not needed.
+ // Not needed for web apps.
'/system/Database/Seeder.php',
'/system/Test/',
- '/system/Language/',
'/system/CLI/',
'/system/Commands/',
'/system/Publisher/',
'/system/ComposerScripts.php',
+ // Not Class/Function files.
+ '/system/Config/Routes.php',
+ '/system/Language/',
+ '/system/bootstrap.php',
+ '/system/rewrite.php',
'/Views/',
// Errors occur.
- '/system/Config/Routes.php',
'/system/ThirdParty/',
],
],
diff --git a/rector.php b/rector.php
index d8ab56f3cc8e..e16de7bae441 100644
--- a/rector.php
+++ b/rector.php
@@ -50,6 +50,7 @@
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
use Rector\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
@@ -213,6 +214,7 @@
ExplicitBoolCompareRector::class,
AddClosureVoidReturnTypeWhereNoReturnRector::class,
AddFunctionVoidReturnTypeWhereNoReturnRector::class,
+ AddMethodCallBasedStrictParamTypeRector::class,
])
->withConfiguredRule(StringClassNameToClassConstantRector::class, [
// keep '\\' prefix string on string '\Foo\Bar'
diff --git a/system/BaseModel.php b/system/BaseModel.php
index 9b8bb70ed482..2b364bdc1917 100644
--- a/system/BaseModel.php
+++ b/system/BaseModel.php
@@ -541,7 +541,7 @@ abstract protected function doReplace(?array $row = null, bool $returnSQL = fals
* Grabs the last error(s) that occurred from the Database connection.
* This method works only with dbCalls.
*
- * @return array|null
+ * @return array
*/
abstract protected function doErrors();
@@ -1242,7 +1242,7 @@ public function replace(?array $row = null, bool $returnSQL = false)
*
* @param bool $forceDB Always grab the db error, not validation
*
- * @return array
+ * @return array
*/
public function errors(bool $forceDB = false)
{
diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php
index bcfb7bd67976..48fbc9d77e23 100644
--- a/system/CodeIgniter.php
+++ b/system/CodeIgniter.php
@@ -56,7 +56,7 @@ class CodeIgniter
/**
* The current version of CodeIgniter Framework
*/
- public const CI_VERSION = '4.5.4';
+ public const CI_VERSION = '4.5.5';
/**
* App startup time.
diff --git a/system/Common.php b/system/Common.php
index 49b3d2896753..dcc9487c588f 100644
--- a/system/Common.php
+++ b/system/Common.php
@@ -28,6 +28,7 @@
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
+use CodeIgniter\Language\Language;
use CodeIgniter\Model;
use CodeIgniter\Session\Session;
use CodeIgniter\Test\TestLogger;
@@ -732,6 +733,7 @@ function is_windows(?bool $mock = null): bool
*/
function lang(string $line, array $args = [], ?string $locale = null)
{
+ /** @var Language $language */
$language = service('language');
// Get active locale
diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php
index 649a201112e4..9d5a02db9f6e 100644
--- a/system/Database/BaseBuilder.php
+++ b/system/Database/BaseBuilder.php
@@ -3164,21 +3164,27 @@ protected function compileWhereHaving(string $qbKey): string
);
foreach ($conditions as &$condition) {
- if (($op = $this->getOperator($condition)) === false
- || ! preg_match('/^(\(?)(.*)(' . preg_quote($op, '/') . ')\s*(.*(?getOperator($condition);
+ if (
+ $op === false
+ || ! preg_match(
+ '/^(\(?)(.*)(' . preg_quote($op, '/') . ')\s*(.*(? '(test <= foo)', /* the whole thing */
- // 1 => '(', /* optional */
- // 2 => 'test', /* the field name */
- // 3 => ' <= ', /* $op */
- // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */
- // 5 => ')' /* optional */
- // );
-
- if (! empty($matches[4])) {
+ // $matches = [
+ // 0 => '(test <= foo)', /* the whole thing */
+ // 1 => '(', /* optional */
+ // 2 => 'test', /* the field name */
+ // 3 => ' <= ', /* $op */
+ // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */
+ // 5 => ')' /* optional */
+ // ];
+
+ if (isset($matches[4]) && $matches[4] !== '') {
$protectIdentifiers = false;
if (str_contains($matches[4], '.')) {
$protectIdentifiers = true;
diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php
index dce399bdefb5..56340c8decd5 100644
--- a/system/Database/BaseConnection.php
+++ b/system/Database/BaseConnection.php
@@ -784,9 +784,9 @@ public function transStart(bool $testMode = false): bool
*
* @return $this
*/
- public function transException(bool $transExcetion)
+ public function transException(bool $transException)
{
- $this->transException = $transExcetion;
+ $this->transException = $transException;
return $this;
}
diff --git a/system/Database/Database.php b/system/Database/Database.php
index c2bc66d30d11..23b6cd0f8d16 100644
--- a/system/Database/Database.php
+++ b/system/Database/Database.php
@@ -13,6 +13,8 @@
namespace CodeIgniter\Database;
+use CodeIgniter\Exceptions\ConfigException;
+use CodeIgniter\Exceptions\CriticalError;
use InvalidArgumentException;
/**
@@ -54,6 +56,8 @@ public function load(array $params = [], string $alias = '')
throw new InvalidArgumentException('You have not selected a database type to connect to.');
}
+ assert($this->checkDbExtension($params['DBDriver']));
+
$this->connections[$alias] = $this->initDriver($params['DBDriver'], 'Connection', $params);
return $this->connections[$alias];
@@ -124,9 +128,9 @@ protected function parseDSN(array $params): array
/**
* Creates a database object.
*
- * @param string $driver Driver name. FQCN can be used.
- * @param string $class 'Connection'|'Forge'|'Utils'
- * @param array|object $argument The constructor parameter.
+ * @param string $driver Driver name. FQCN can be used.
+ * @param string $class 'Connection'|'Forge'|'Utils'
+ * @param array|ConnectionInterface $argument The constructor parameter or DB connection
*
* @return BaseConnection|BaseUtils|Forge
*/
@@ -138,4 +142,43 @@ protected function initDriver(string $driver, string $class, $argument): object
return new $classname($argument);
}
+
+ /**
+ * Check the PHP database extension is loaded.
+ *
+ * @param string $driver DB driver or FQCN for custom driver
+ */
+ private function checkDbExtension(string $driver): bool
+ {
+ if (str_contains($driver, '\\')) {
+ // Cannot check a fully qualified classname for a custom driver.
+ return true;
+ }
+
+ $extensionMap = [
+ // DBDriver => PHP extension
+ 'MySQLi' => 'mysqli',
+ 'SQLite3' => 'sqlite3',
+ 'Postgre' => 'pgsql',
+ 'SQLSRV' => 'sqlsrv',
+ 'OCI8' => 'oci8',
+ ];
+
+ $extension = $extensionMap[$driver] ?? '';
+
+ if ($extension === '') {
+ $message = 'Invalid DBDriver name: "' . $driver . '"';
+
+ throw new ConfigException($message);
+ }
+
+ if (extension_loaded($extension)) {
+ return true;
+ }
+
+ $message = 'The required PHP extension "' . $extension . '" is not loaded.'
+ . ' Install and enable it to use "' . $driver . '" driver.';
+
+ throw new CriticalError($message);
+ }
}
diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php
index 1071dfedf06b..0d2dce0957ae 100644
--- a/system/Database/Postgre/Builder.php
+++ b/system/Database/Postgre/Builder.php
@@ -411,10 +411,8 @@ static function ($key, $value) use ($table, $alias, $that) {
* Returns cast expression.
*
* @TODO move this to BaseBuilder in 4.5.0
- *
- * @param float|int|string $expression
*/
- private function cast($expression, ?string $type): string
+ private function cast(string $expression, ?string $type): string
{
return ($type === null) ? $expression : 'CAST(' . $expression . ' AS ' . strtoupper($type) . ')';
}
diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php
index 5de8a42187a4..464992af6251 100644
--- a/system/Entity/Entity.php
+++ b/system/Entity/Entity.php
@@ -106,7 +106,7 @@ class Entity implements JsonSerializable
/**
* Holds the current values of all class vars.
*
- * @var array
+ * @var array
*/
protected $attributes = [];
@@ -115,7 +115,7 @@ class Entity implements JsonSerializable
* what's actually been changed and not accidentally write
* nulls where we shouldn't.
*
- * @var array
+ * @var array
*/
protected $original = [];
diff --git a/system/Filters/CSRF.php b/system/Filters/CSRF.php
index 90ccb9b501a0..beb15f62b03a 100644
--- a/system/Filters/CSRF.php
+++ b/system/Filters/CSRF.php
@@ -18,6 +18,7 @@
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Security\Exceptions\SecurityException;
+use CodeIgniter\Security\Security;
/**
* CSRF filter.
@@ -30,14 +31,7 @@
class CSRF implements FilterInterface
{
/**
- * Do whatever processing this filter needs to do.
- * By default it should not return anything during
- * normal execution. However, when an abnormal state
- * is found, it should return an instance of
- * CodeIgniter\HTTP\Response. If it does, script
- * execution will end and that Response will be
- * sent back to the client, allowing for error pages,
- * redirects, etc.
+ * CSRF verification.
*
* @param list|null $arguments
*
@@ -51,6 +45,7 @@ public function before(RequestInterface $request, $arguments = null)
return;
}
+ /** @var Security $security */
$security = service('security');
try {
diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php
index 945c3e08d3ce..7582bc467733 100644
--- a/system/HTTP/ContentSecurityPolicy.php
+++ b/system/HTTP/ContentSecurityPolicy.php
@@ -31,7 +31,7 @@ class ContentSecurityPolicy
/**
* CSP directives
*
- * @var array
+ * @var array [name => property]
*/
protected array $directives = [
'base-uri' => 'baseURI',
@@ -166,7 +166,8 @@ class ContentSecurityPolicy
protected $sandbox = [];
/**
- * Used for security enforcement
+ * A set of endpoints to which csp violation reports will be sent when
+ * particular behaviors are prevented.
*
* @var string|null
*/
@@ -189,7 +190,7 @@ class ContentSecurityPolicy
/**
* Used for security enforcement
*
- * @var array
+ * @var list
*/
protected $validSources = [
'self',
@@ -242,7 +243,7 @@ class ContentSecurityPolicy
/**
* An array of header info since we have
- * to build ourself before passing to Response.
+ * to build ourselves before passing to Response.
*
* @var array
*/
@@ -594,6 +595,9 @@ public function addPluginType($mime, ?bool $explicitReporting = null)
*
* @see http://www.w3.org/TR/CSP/#directive-report-uri
*
+ * @param string $uri URL to send reports. Set `''` if you want to remove
+ * this directive at runtime.
+ *
* @return $this
*/
public function setReportURI(string $uri)
diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php
index f3d33611dcef..b9e5e5c8ed0e 100644
--- a/system/Helpers/html_helper.php
+++ b/system/Helpers/html_helper.php
@@ -21,9 +21,10 @@
/**
* Unordered List
*
- * Generates an HTML unordered list from an single or
- * multi-dimensional array.
+ * Generates an HTML unordered list from a single or
+ * multidimensional array.
*
+ * @param array $list List entries
* @param array|object|string $attributes HTML attributes string, array, object
*/
function ul(array $list, $attributes = ''): string
@@ -36,8 +37,9 @@ function ul(array $list, $attributes = ''): string
/**
* Ordered List
*
- * Generates an HTML ordered list from an single or multi-dimensional array.
+ * Generates an HTML ordered list from a single or multidimensional array.
*
+ * @param array $list List entries
* @param array|object|string $attributes HTML attributes string, array, object
*/
function ol(array $list, $attributes = ''): string
@@ -50,10 +52,10 @@ function ol(array $list, $attributes = ''): string
/**
* Generates the list
*
- * Generates an HTML ordered list from an single or multi-dimensional array.
+ * Generates an HTML ordered list from a single or multidimensional array.
*
- * @param array $list
- * @param array|object|string $attributes string, array, object
+ * @param array $list List entries
+ * @param array|object|string $attributes HTML attributes string, array, object
*/
function _list(string $type = 'ul', $list = [], $attributes = '', int $depth = 0): string
{
@@ -92,7 +94,7 @@ function _list(string $type = 'ul', $list = [], $attributes = '', int $depth = 0
* Generates an image element
*
* @param array|string $src Image source URI, or array of attributes and values
- * @param bool $indexPage Whether to treat $src as a routed URI string
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the source path
* @param array|object|string $attributes Additional HTML attributes
*/
function img($src = '', bool $indexPage = false, $attributes = ''): string
@@ -192,7 +194,7 @@ function doctype(string $type = 'html5'): string
* Generates link to a JS file
*
* @param array|string $src Script source or an array of attributes
- * @param bool $indexPage Should indexPage be added to the JS path
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the JS path
*/
function script_tag($src = '', bool $indexPage = false): string
{
@@ -227,7 +229,7 @@ function script_tag($src = '', bool $indexPage = false): string
* Generates link tag
*
* @param array|string $href Stylesheet href or an array
- * @param bool $indexPage should indexPage be added to the CSS path.
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the CSS path.
*/
function link_tag(
$href = '',
@@ -288,6 +290,7 @@ function link_tag(
* @param array|string $src Either a source string or an array of sources
* @param string $unsupportedMessage The message to display if the media tag is not supported by the browser
* @param string $attributes HTML attributes
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the source path
*/
function video($src, string $unsupportedMessage = '', string $attributes = '', array $tracks = [], bool $indexPage = false): string
{
@@ -334,6 +337,7 @@ function video($src, string $unsupportedMessage = '', string $attributes = '', a
* @param array|string $src Either a source string or an array of sources
* @param string $unsupportedMessage The message to display if the media tag is not supported by the browser.
* @param string $attributes HTML attributes
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the source path
*/
function audio($src, string $unsupportedMessage = '', string $attributes = '', array $tracks = [], bool $indexPage = false): string
{
@@ -413,6 +417,7 @@ function _media(string $name, array $types = [], string $unsupportedMessage = ''
* @param string $src The path of the media resource
* @param string $type The MIME-type of the resource with optional codecs parameters
* @param string $attributes HTML attributes
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the source path
*/
function source(string $src, string $type = 'unknown', string $attributes = '', bool $indexPage = false): string
{
@@ -438,7 +443,10 @@ function source(string $src, string $type = 'unknown', string $attributes = '',
* Generates a track element to specify timed tracks. The tracks are
* formatted in WebVTT format.
*
- * @param string $src The path of the .VTT file
+ * @param string $src The path of the .VTT file
+ * @param string $kind How the text track is meant to be used
+ * @param string $srcLanguage Language of the track text data
+ * @param string $label A user-readable title of the text track
*/
function track(string $src, string $kind, string $srcLanguage, string $label): string
{
@@ -461,6 +469,7 @@ function track(string $src, string $kind, string $srcLanguage, string $label): s
* @param string $data A resource URL
* @param string $type Content-type of the resource
* @param string $attributes HTML attributes
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the data path
*/
function object(string $data, string $type = 'unknown', string $attributes = '', array $params = [], bool $indexPage = false): string
{
@@ -513,6 +522,7 @@ function param(string $name, string $value, string $type = 'ref', string $attrib
* @param string $src The path of the resource to embed
* @param string $type MIME-type
* @param string $attributes HTML attributes
+ * @param bool $indexPage Should `Config\App::$indexPage` be added to the source path
*/
function embed(string $src, string $type = 'unknown', string $attributes = '', bool $indexPage = false): string
{
diff --git a/system/Helpers/text_helper.php b/system/Helpers/text_helper.php
index ae6b6f111c81..3de105736f57 100644
--- a/system/Helpers/text_helper.php
+++ b/system/Helpers/text_helper.php
@@ -526,9 +526,10 @@ function reduce_double_slashes(string $str): string
*/
function reduce_multiples(string $str, string $character = ',', bool $trim = false): string
{
- $str = preg_replace('#' . preg_quote($character, '#') . '{2,}#', $character, $str);
+ $pattern = '#' . preg_quote($character, '#') . '{2,}#';
+ $str = preg_replace($pattern, $character, $str);
- return ($trim) ? trim($str, $character) : $str;
+ return $trim ? trim($str, $character) : $str;
}
}
diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php
index 1d03132d82cf..d68fea2aad7d 100644
--- a/system/Helpers/url_helper.php
+++ b/system/Helpers/url_helper.php
@@ -351,7 +351,7 @@ function safe_mailto(string $email, string $title = '', $attributes = ''): strin
function auto_link(string $str, string $type = 'both', bool $popup = false): string
{
// Find and replace any URLs.
- if ($type !== 'email' && preg_match_all('#(\w*://|www\.)[^\s()<>;]+\w#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+ if ($type !== 'email' && preg_match_all('#(\w*://|www\.)[a-z0-9]+(-+[a-z0-9]+)*(\.[a-z0-9]+(-+[a-z0-9]+)*)+(/([^\s()<>;]+\w)?/?)?#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
// Set our target HTML if using popup links.
$target = ($popup) ? ' target="_blank"' : '';
diff --git a/system/Router/Router.php b/system/Router/Router.php
index dc778cd03ffe..d60313f6c032 100644
--- a/system/Router/Router.php
+++ b/system/Router/Router.php
@@ -181,7 +181,7 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request
}
/**
- * Finds the controller method corresponding to the URI.
+ * Finds the controller corresponding to the URI.
*
* @param string|null $uri URI path relative to baseURL
*
@@ -241,7 +241,7 @@ public function getFilters(): array
}
/**
- * Returns the name of the matched controller.
+ * Returns the name of the matched controller or closure.
*
* @return (Closure(mixed...): (ResponseInterface|string|void))|string Controller classname or Closure
*/
@@ -254,7 +254,7 @@ public function controllerName()
/**
* Returns the name of the method to run in the
- * chosen container.
+ * chosen controller.
*/
public function methodName(): string
{
diff --git a/system/Security/Security.php b/system/Security/Security.php
index 077b3b9cdbb0..0b51062e4cdb 100644
--- a/system/Security/Security.php
+++ b/system/Security/Security.php
@@ -233,7 +233,7 @@ private function configureCookie(CookieConfig $cookie): void
}
/**
- * CSRF Verify
+ * CSRF verification.
*
* @return $this
*
diff --git a/system/ThirdParty/PSR/Log/LoggerAwareInterface.php b/system/ThirdParty/PSR/Log/LoggerAwareInterface.php
index cc46a95147cd..062187057d51 100644
--- a/system/ThirdParty/PSR/Log/LoggerAwareInterface.php
+++ b/system/ThirdParty/PSR/Log/LoggerAwareInterface.php
@@ -9,10 +9,6 @@ interface LoggerAwareInterface
{
/**
* Sets a logger instance on the object.
- *
- * @param LoggerInterface $logger
- *
- * @return void
*/
public function setLogger(LoggerInterface $logger): void;
}
diff --git a/system/ThirdParty/PSR/Log/LoggerAwareTrait.php b/system/ThirdParty/PSR/Log/LoggerAwareTrait.php
index 4fb57a292263..85104dbc19e3 100644
--- a/system/ThirdParty/PSR/Log/LoggerAwareTrait.php
+++ b/system/ThirdParty/PSR/Log/LoggerAwareTrait.php
@@ -9,15 +9,11 @@ trait LoggerAwareTrait
{
/**
* The logger instance.
- *
- * @var LoggerInterface|null
*/
protected ?LoggerInterface $logger = null;
/**
* Sets a logger.
- *
- * @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger): void
{
diff --git a/system/ThirdParty/PSR/Log/LoggerInterface.php b/system/ThirdParty/PSR/Log/LoggerInterface.php
index b3a24b5f7e9d..8afabc90c820 100644
--- a/system/ThirdParty/PSR/Log/LoggerInterface.php
+++ b/system/ThirdParty/PSR/Log/LoggerInterface.php
@@ -22,10 +22,7 @@ interface LoggerInterface
/**
* System is unusable.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function emergency(string|\Stringable $message, array $context = []): void;
@@ -35,10 +32,7 @@ public function emergency(string|\Stringable $message, array $context = []): voi
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function alert(string|\Stringable $message, array $context = []): void;
@@ -47,10 +41,7 @@ public function alert(string|\Stringable $message, array $context = []): void;
*
* Example: Application component unavailable, unexpected exception.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function critical(string|\Stringable $message, array $context = []): void;
@@ -58,10 +49,7 @@ public function critical(string|\Stringable $message, array $context = []): void
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function error(string|\Stringable $message, array $context = []): void;
@@ -71,20 +59,14 @@ public function error(string|\Stringable $message, array $context = []): void;
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function warning(string|\Stringable $message, array $context = []): void;
/**
* Normal but significant events.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function notice(string|\Stringable $message, array $context = []): void;
@@ -93,32 +75,22 @@ public function notice(string|\Stringable $message, array $context = []): void;
*
* Example: User logs in, SQL logs.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function info(string|\Stringable $message, array $context = []): void;
/**
* Detailed debug information.
*
- * @param string|\Stringable $message
* @param mixed[] $context
- *
- * @return void
*/
public function debug(string|\Stringable $message, array $context = []): void;
/**
* Logs with an arbitrary level.
*
- * @param mixed $level
- * @param string|\Stringable $message
* @param mixed[] $context
*
- * @return void
- *
* @throws \Psr\Log\InvalidArgumentException
*/
public function log($level, string|\Stringable $message, array $context = []): void;
diff --git a/system/ThirdParty/PSR/Log/LoggerTrait.php b/system/ThirdParty/PSR/Log/LoggerTrait.php
index 9c8733f957fd..a5d9980b6fa4 100644
--- a/system/ThirdParty/PSR/Log/LoggerTrait.php
+++ b/system/ThirdParty/PSR/Log/LoggerTrait.php
@@ -14,11 +14,6 @@ trait LoggerTrait
{
/**
* System is unusable.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function emergency(string|\Stringable $message, array $context = []): void
{
@@ -30,11 +25,6 @@ public function emergency(string|\Stringable $message, array $context = []): voi
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function alert(string|\Stringable $message, array $context = []): void
{
@@ -45,11 +35,6 @@ public function alert(string|\Stringable $message, array $context = []): void
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function critical(string|\Stringable $message, array $context = []): void
{
@@ -59,11 +44,6 @@ public function critical(string|\Stringable $message, array $context = []): void
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function error(string|\Stringable $message, array $context = []): void
{
@@ -75,11 +55,6 @@ public function error(string|\Stringable $message, array $context = []): void
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function warning(string|\Stringable $message, array $context = []): void
{
@@ -88,11 +63,6 @@ public function warning(string|\Stringable $message, array $context = []): void
/**
* Normal but significant events.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function notice(string|\Stringable $message, array $context = []): void
{
@@ -103,11 +73,6 @@ public function notice(string|\Stringable $message, array $context = []): void
* Interesting events.
*
* Example: User logs in, SQL logs.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function info(string|\Stringable $message, array $context = []): void
{
@@ -116,11 +81,6 @@ public function info(string|\Stringable $message, array $context = []): void
/**
* Detailed debug information.
- *
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
*/
public function debug(string|\Stringable $message, array $context = []): void
{
@@ -130,11 +90,7 @@ public function debug(string|\Stringable $message, array $context = []): void
/**
* Logs with an arbitrary level.
*
- * @param mixed $level
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
+ * @param mixed $level
*
* @throws \Psr\Log\InvalidArgumentException
*/
diff --git a/system/ThirdParty/PSR/Log/NullLogger.php b/system/ThirdParty/PSR/Log/NullLogger.php
index c1cc3c0692a2..de0561e2ae9c 100644
--- a/system/ThirdParty/PSR/Log/NullLogger.php
+++ b/system/ThirdParty/PSR/Log/NullLogger.php
@@ -15,11 +15,7 @@ class NullLogger extends AbstractLogger
/**
* Logs with an arbitrary level.
*
- * @param mixed $level
- * @param string|\Stringable $message
- * @param array $context
- *
- * @return void
+ * @param mixed[] $context
*
* @throws \Psr\Log\InvalidArgumentException
*/
diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php
index e4fc4fc91142..ec02a4e0a0ac 100644
--- a/system/Validation/StrictRules/Rules.php
+++ b/system/Validation/StrictRules/Rules.php
@@ -48,11 +48,15 @@ public function differs(
return $str !== dot_array_search($otherField, $data);
}
- if (! array_key_exists($field, $data)) {
+ if (! array_key_exists($otherField, $data)) {
return false;
}
- if (! array_key_exists($otherField, $data)) {
+ if (str_contains($field, '.')) {
+ if (! ArrayHelper::dotKeyExists($field, $data)) {
+ return false;
+ }
+ } elseif (! array_key_exists($field, $data)) {
return false;
}
@@ -281,11 +285,15 @@ public function matches(
return $str === dot_array_search($otherField, $data);
}
- if (! array_key_exists($field, $data)) {
+ if (! array_key_exists($otherField, $data)) {
return false;
}
- if (! array_key_exists($otherField, $data)) {
+ if (str_contains($field, '.')) {
+ if (! ArrayHelper::dotKeyExists($field, $data)) {
+ return false;
+ }
+ } elseif (! array_key_exists($field, $data)) {
return false;
}
diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php
index bfc661c2b3d4..a4d817d2a9b4 100644
--- a/system/Validation/Validation.php
+++ b/system/Validation/Validation.php
@@ -51,7 +51,7 @@ class Validation implements ValidationInterface
/**
* Stores the actual rules that should be run against $data.
*
- * @var array
+ * @var array}>
*
* [
* field1 => [
@@ -163,6 +163,9 @@ public function run(?array $data = null, ?string $group = null, $dbGroup = null)
// Run through each rule. If we have any field set for
// this rule, then we need to run them through!
foreach ($this->rules as $field => $setup) {
+ // An array key might be int.
+ $field = (string) $field;
+
$rules = $setup['rules'];
if (is_string($rules)) {
diff --git a/tests/system/API/ResponseTraitTest.php b/tests/system/API/ResponseTraitTest.php
index 0887c673d616..82229b014bd4 100644
--- a/tests/system/API/ResponseTraitTest.php
+++ b/tests/system/API/ResponseTraitTest.php
@@ -574,7 +574,7 @@ public function testValidContentTypes(): void
}
}
- private function tryValidContentType($mimeType, $contentType): void
+ private function tryValidContentType(string $mimeType, string $contentType): void
{
$original = $_SERVER;
$_SERVER['CONTENT_TYPE'] = $mimeType;
diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php
index 04dfe04e991f..8529d79dc118 100644
--- a/tests/system/Autoloader/AutoloaderTest.php
+++ b/tests/system/Autoloader/AutoloaderTest.php
@@ -25,6 +25,7 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
+use PHPUnit\Framework\Attributes\WithoutErrorHandler;
use RuntimeException;
use UnnamespacedClass;
@@ -394,12 +395,9 @@ public function testAutoloaderLoadsNonClassFiles(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testLoadHelpers(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$config = new Autoload();
$config->helpers[] = 'form';
diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php
index a550e098e9b0..d87d0d8661ba 100644
--- a/tests/system/CodeIgniterTest.php
+++ b/tests/system/CodeIgniterTest.php
@@ -34,6 +34,7 @@
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+use PHPUnit\Framework\Attributes\WithoutErrorHandler;
use Tests\Support\Filters\Customfilter;
use Tests\Support\Filters\RedirectFilter;
@@ -48,15 +49,12 @@ final class CodeIgniterTest extends CIUnitTestCase
private CodeIgniter $codeigniter;
protected $routes;
+ #[WithoutErrorHandler]
protected function setUp(): void
{
parent::setUp();
$this->resetServices();
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$this->codeigniter = new MockCodeIgniter(new App());
diff --git a/tests/system/Commands/GenerateKeyTest.php b/tests/system/Commands/GenerateKeyTest.php
index 289a294f5d0c..40df8d1477c3 100644
--- a/tests/system/Commands/GenerateKeyTest.php
+++ b/tests/system/Commands/GenerateKeyTest.php
@@ -19,6 +19,7 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
+use PHPUnit\Framework\Attributes\WithoutErrorHandler;
/**
* @internal
@@ -31,6 +32,7 @@ final class GenerateKeyTest extends CIUnitTestCase
private string $envPath;
private string $backupEnvPath;
+ #[WithoutErrorHandler]
protected function setUp(): void
{
parent::setUp();
@@ -43,10 +45,6 @@ protected function setUp(): void
}
$this->resetEnvironment();
-
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
}
protected function tearDown(): void
diff --git a/tests/system/CommonFunctionsSendTest.php b/tests/system/CommonFunctionsSendTest.php
index 46871c6284df..e1f3f9dff961 100644
--- a/tests/system/CommonFunctionsSendTest.php
+++ b/tests/system/CommonFunctionsSendTest.php
@@ -17,6 +17,7 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
+use PHPUnit\Framework\Attributes\WithoutErrorHandler;
/**
* @internal
@@ -24,15 +25,12 @@
#[Group('SeparateProcess')]
final class CommonFunctionsSendTest extends CIUnitTestCase
{
+ #[WithoutErrorHandler]
protected function setUp(): void
{
parent::setUp();
unset($_ENV['foo'], $_SERVER['foo']);
-
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
}
/**
diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php
index fbbece86ddd1..faa96e1fea4e 100644
--- a/tests/system/CommonFunctionsTest.php
+++ b/tests/system/CommonFunctionsTest.php
@@ -46,6 +46,7 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
+use PHPUnit\Framework\Attributes\WithoutErrorHandler;
use RuntimeException;
use stdClass;
use Tests\Support\Models\JobModel;
@@ -274,12 +275,9 @@ public function testEscapeRecursiveArrayRaw(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testSessionInstance(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
$this->assertInstanceOf(Session::class, session());
@@ -287,12 +285,9 @@ public function testSessionInstance(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testSessionVariable(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
$_SESSION['notbogus'] = 'Hi there';
@@ -302,12 +297,9 @@ public function testSessionVariable(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testSessionVariableNotThere(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
$_SESSION['bogus'] = 'Hi there';
@@ -428,12 +420,9 @@ public function testModelExistsAbsoluteClassname(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testOldInput(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
// setup from RedirectResponseTest...
$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -465,12 +454,9 @@ public function testOldInput(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testOldInputSerializeData(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
// setup from RedirectResponseTest...
$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -503,12 +489,9 @@ public function testOldInputSerializeData(): void
*/
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testOldInputArray(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->injectSessionMock();
// setup from RedirectResponseTest...
$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -622,12 +605,9 @@ public function testRedirectResponseCookies1(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testTrace(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
ob_start();
trace();
$content = ob_get_clean();
@@ -647,12 +627,9 @@ public function testViewNotSaveData(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testForceHttpsNullRequestAndResponse(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->assertNull(Services::response()->header('Location'));
Services::response()->setCookie('force', 'cookie');
@@ -763,12 +740,9 @@ public function testDWithCSP(): void
#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
+ #[WithoutErrorHandler]
public function testTraceWithCSP(): void
{
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- restore_error_handler();
-
$this->resetServices();
/** @var App $config */
@@ -780,11 +754,6 @@ public function testTraceWithCSP(): void
Kint::$cli_detection = false;
- // Workaround for errors on PHPUnit 10 and PHP 8.3.
- // See https://github.com/sebastianbergmann/phpunit/issues/5403#issuecomment-1906810619
- // `$app->initialize()` sets error handler.
- restore_error_handler();
-
$this->expectOutputRegex('/