diff --git a/.github/workflows/deploy-apidocs.yml b/.github/workflows/deploy-apidocs.yml index 5675963ea24f..cc7015200e13 100644 --- a/.github/workflows/deploy-apidocs.yml +++ b/.github/workflows/deploy-apidocs.yml @@ -29,12 +29,12 @@ jobs: git config --global user.name "${GITHUB_ACTOR}" - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: source - name: Checkout target - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: codeigniter4/api token: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/deploy-distributables.yml b/.github/workflows/deploy-distributables.yml index e279abad2dd7..3d4b31f289e6 100644 --- a/.github/workflows/deploy-distributables.yml +++ b/.github/workflows/deploy-distributables.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # fetch all tags @@ -48,12 +48,12 @@ jobs: git config --global user.name "${GITHUB_ACTOR}" - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: source - name: Checkout target - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: codeigniter4/framework token: ${{ secrets.ACCESS_TOKEN }} @@ -98,12 +98,12 @@ jobs: git config --global user.name "${GITHUB_ACTOR}" - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: source - name: Checkout target - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: codeigniter4/appstarter token: ${{ secrets.ACCESS_TOKEN }} @@ -148,12 +148,12 @@ jobs: git config --global user.name "${GITHUB_ACTOR}" - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: source - name: Checkout target - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: codeigniter4/userguide token: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml index 58e5e731ed18..ab366268e5ee 100644 --- a/.github/workflows/deploy-userguide-latest.yml +++ b/.github/workflows/deploy-userguide-latest.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/reusable-coveralls.yml b/.github/workflows/reusable-coveralls.yml index 984b657a623e..f545d79bb5ec 100644 --- a/.github/workflows/reusable-coveralls.yml +++ b/.github/workflows/reusable-coveralls.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/reusable-phpunit-test.yml b/.github/workflows/reusable-phpunit-test.yml index bea8e4c3bee6..0963f5865242 100644 --- a/.github/workflows/reusable-phpunit-test.yml +++ b/.github/workflows/reusable-phpunit-test.yml @@ -149,7 +149,7 @@ jobs: sudo apt-get install --fix-broken - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/reusable-serviceless-phpunit-test.yml b/.github/workflows/reusable-serviceless-phpunit-test.yml index c044f2178ea3..6371847c77bd 100644 --- a/.github/workflows/reusable-serviceless-phpunit-test.yml +++ b/.github/workflows/reusable-serviceless-phpunit-test.yml @@ -61,7 +61,7 @@ jobs: sudo apt-get install --fix-broken - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-autoreview.yml b/.github/workflows/test-autoreview.yml index 21f885445b63..44feb4e33139 100644 --- a/.github/workflows/test-autoreview.yml +++ b/.github/workflows/test-autoreview.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-coding-standards.yml b/.github/workflows/test-coding-standards.yml index a38e3ab85cd9..9217f48bb127 100644 --- a/.github/workflows/test-coding-standards.yml +++ b/.github/workflows/test-coding-standards.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-deptrac.yml b/.github/workflows/test-deptrac.yml index e6b92346972b..b92f861c790c 100644 --- a/.github/workflows/test-deptrac.yml +++ b/.github/workflows/test-deptrac.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-phpcpd.yml b/.github/workflows/test-phpcpd.yml index f08945514386..53daa8ebab95 100644 --- a/.github/workflows/test-phpcpd.yml +++ b/.github/workflows/test-phpcpd.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index 8e2a57685cba..3ab82f25c762 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-psalm.yml b/.github/workflows/test-psalm.yml index da7c6e422745..62e65bf7a866 100644 --- a/.github/workflows/test-psalm.yml +++ b/.github/workflows/test-psalm.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml index 7df4a3a3d3ed..7912a59f5d3f 100644 --- a/.github/workflows/test-rector.yml +++ b/.github/workflows/test-rector.yml @@ -53,7 +53,7 @@ jobs: - utils steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/test-scss.yml b/.github/workflows/test-scss.yml index 26a679719156..417eede8f344 100644 --- a/.github/workflows/test-scss.yml +++ b/.github/workflows/test-scss.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3.0.0 diff --git a/.github/workflows/test-userguide.yml b/.github/workflows/test-userguide.yml index b188080996db..b127d77dd344 100644 --- a/.github/workflows/test-userguide.yml +++ b/.github/workflows/test-userguide.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Detect usage of tabs in RST files run: php utils/check_tabs_in_rst.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 59da5951f9a4..dc2b5bc5640c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [v4.4.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.4.1) (2023-09-05) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.4.0...v4.4.1) + +### Fixed Bugs + +* docs: add missing Config updates for Hot Reloading by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7862 +* fix: auto route legacy does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7871 +* fix: Factories may not return shared instance by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7868 +* fix: replace `config(DocTypes::class)` with `new DocTypes()` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7872 +* fix: FeatureTest may cause risky tests by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7867 +* fix: reverse routing causes ErrorException by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7880 +* fix: Email library forces to switch to TLS when setting port 465 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7883 +* fix: [DebugBar] make CSS rotate class less broad by @sanchawebo in https://github.com/codeigniter4/CodeIgniter4/pull/7882 +* fix: FeatureTest fails when forceGlobalSecureRequests is true by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7890 + ## [v4.4.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.4.0) (2023-08-25) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.8...v4.4.0) diff --git a/admin/RELEASE.md b/admin/RELEASE.md index b625c743a75b..226f0ad3397c 100644 --- a/admin/RELEASE.md +++ b/admin/RELEASE.md @@ -11,6 +11,8 @@ If you release a new minor version. * Create PR to merge `4.x` into `develop` and merge it +* Rename the current minor version (e.g., `4.4`) in Setting > Branches > + "Branch protection rules" to the next minor version. E.g. `4.4` → `4.5` * Delete the merged `4.x` branch (This closes all PRs to the branch) * Do the regular release process. Go to the next "Changelog" section @@ -77,6 +79,7 @@ the existing content. * fill in the "All Changes" section, and add it to **upgrading.rst** * git diff --name-status origin/master -- . ':!system' * Remove the section titles that have no items + * Update the "from" version in the title. E.g., `from 4.3.x` → `from 4.3.8` * Commit the changes with `Prep for 4.x.x release` and push to origin * Create a new PR from `release-4.x.x` to `develop`: * Title: `Prep for 4.x.x release` @@ -113,6 +116,8 @@ the existing content. * "[Deploy Distributable Repos](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/deploy-distributables.yml)", the main repo * "[Deploy Production](https://github.com/codeigniter4/userguide/actions/workflows/deploy.yml)", UG repo * "[pages-build-deployment](https://github.com/codeigniter4/userguide/actions/workflows/pages/pages-build-deployment)", UG repo + * Check if "CodeIgniter4.x.x.epub" is added to UG repo. "CodeIgniter.epub" was + created when v4.3.8 was released. * Fast-forward `develop` branch to catch the merge commit from `master` ```console git fetch origin diff --git a/admin/css/debug-toolbar/toolbar.scss b/admin/css/debug-toolbar/toolbar.scss index 01dfd2207b15..5d809874ebee 100644 --- a/admin/css/debug-toolbar/toolbar.scss +++ b/admin/css/debug-toolbar/toolbar.scss @@ -202,6 +202,15 @@ // Give room for OS X scrollbar white-space: nowrap; z-index: 10000; + // Endless rotate + .rotate { + animation: toolbar-rotate 9s linear infinite; + } + @keyframes toolbar-rotate { + to { + transform: rotate(360deg); + } + } } // Fixed top @@ -501,13 +510,3 @@ .debug-bar-noverflow { overflow: hidden; } - -/* ENDLESS ROTATE */ -.rotate { - animation: rotate 9s linear infinite; -} -@keyframes rotate { - to { - transform: rotate(360deg); - } -} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index e9ee6613d22b..22f05ecdab26 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -17,6 +17,8 @@ * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. + * + * @immutable */ class Autoload extends AutoloadConfig { diff --git a/app/Config/DocTypes.php b/app/Config/DocTypes.php index 788d68fdc117..7e8aaacf09dd 100755 --- a/app/Config/DocTypes.php +++ b/app/Config/DocTypes.php @@ -2,6 +2,9 @@ namespace Config; +/** + * @immutable + */ class DocTypes { /** diff --git a/app/Config/Email.php b/app/Config/Email.php index 01350186a3e9..01b805a5c1fa 100644 --- a/app/Config/Email.php +++ b/app/Config/Email.php @@ -56,7 +56,11 @@ class Email extends BaseConfig public bool $SMTPKeepAlive = false; /** - * SMTP Encryption. Either tls or ssl + * SMTP Encryption. + * + * @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command + * to the server. 'ssl' means implicit SSL. Connection on port + * 465 should set this to ''. */ public string $SMTPCrypto = 'tls'; diff --git a/app/Config/ForeignCharacters.php b/app/Config/ForeignCharacters.php index 174ddb16a872..f1a95725c5c7 100644 --- a/app/Config/ForeignCharacters.php +++ b/app/Config/ForeignCharacters.php @@ -4,6 +4,9 @@ use CodeIgniter\Config\ForeignCharacters as BaseForeignCharacters; +/** + * @immutable + */ class ForeignCharacters extends BaseForeignCharacters { } diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index 99d28e5f8f86..d02df1aba796 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -15,6 +15,8 @@ * * When working with mime types, please make sure you have the ´fileinfo´ * extension enabled to reliably detect the media types. + * + * @immutable */ class Mimes { diff --git a/app/Config/Modules.php b/app/Config/Modules.php index 54079e771f29..f84580c856d3 100644 --- a/app/Config/Modules.php +++ b/app/Config/Modules.php @@ -9,6 +9,8 @@ * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. + * + * @immutable */ class Modules extends BaseModules { diff --git a/composer.json b/composer.json index 997ca73ebb89..9421a652fb7a 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "phpunit/phpcov": "^8.2", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1 || ^2.0", - "rector/rector": "0.18.0", + "rector/rector": "0.18.1", "vimeo/psalm": "^5.0" }, "suggest": { diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 9dea6edd8884..ec5d6987990e 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -16,61 +16,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/BaseModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\BaseHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/BaseHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\BaseHandler\\:\\:remember\\(\\) has parameter \\$callback with no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/BaseHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\DummyHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/DummyHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\DummyHandler\\:\\:remember\\(\\) has parameter \\$callback with no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/DummyHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\FileHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/FileHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\MemcachedHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/MemcachedHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\PredisHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/PredisHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\RedisHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/RedisHandler.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Cache\\\\Handlers\\\\WincacheHandler\\:\\:deleteMatching\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Cache/Handlers/WincacheHandler.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\CodeIgniter\\:\\:bootstrapEnvironment\\(\\) has no return type specified\\.$#', 'count' => 1, 'path' => __DIR__ . '/system/CodeIgniter.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\CodeIgniter\\:\\:cache\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/CodeIgniter.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\CodeIgniter\\:\\:callExit\\(\\) has no return type specified\\.$#', 'count' => 1, @@ -1436,11 +1386,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/Mock/MockCache.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Test\\\\Mock\\\\MockCache\\:\\:remember\\(\\) has parameter \\$callback with no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Test/Mock/MockCache.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Test\\\\Mock\\\\MockCodeIgniter\\:\\:callExit\\(\\) has no return type specified\\.$#', 'count' => 1, diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 044f9dbbc8fd..112a662d35ac 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -177,7 +177,7 @@ public static function init() * Named options must be in the following formats: * php index.php user -v --v -name=John --name=John * - * @param string $prefix + * @param string $prefix You may specify a string with which to prompt the user. */ public static function input(?string $prefix = null): string { diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 9f700370fc8a..74a4482cd4d9 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -77,6 +77,7 @@ public static function validateKey($key, $prefix = ''): string * @param string $key Cache item name * @param int $ttl Time to live * @param Closure $callback Callback return value + * @phpstan-param Closure(): mixed $callback * * @return array|bool|float|int|object|string|null */ @@ -98,6 +99,8 @@ public function remember(string $key, int $ttl, Closure $callback) * * @param string $pattern Cache items glob-style pattern * + * @return int|never + * * @throws Exception */ public function deleteMatching(string $pattern) diff --git a/system/Cache/Handlers/DummyHandler.php b/system/Cache/Handlers/DummyHandler.php index fd4f1b503111..4349db4f39a5 100644 --- a/system/Cache/Handlers/DummyHandler.php +++ b/system/Cache/Handlers/DummyHandler.php @@ -59,6 +59,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return int */ public function deleteMatching(string $pattern) { diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index 524ee8e47720..aea0a605c47f 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -127,6 +127,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return int */ public function deleteMatching(string $pattern) { diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index f38a541113a1..a998ad30386f 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -188,6 +188,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return never */ public function deleteMatching(string $pattern) { diff --git a/system/Cache/Handlers/PredisHandler.php b/system/Cache/Handlers/PredisHandler.php index ab4fb1ff0a46..e549a7422c91 100644 --- a/system/Cache/Handlers/PredisHandler.php +++ b/system/Cache/Handlers/PredisHandler.php @@ -150,6 +150,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return int */ public function deleteMatching(string $pattern) { diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index adb9a581a75a..d86d9f980d41 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -176,6 +176,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return int */ public function deleteMatching(string $pattern) { diff --git a/system/Cache/Handlers/WincacheHandler.php b/system/Cache/Handlers/WincacheHandler.php index c5f0f08467e9..b1ea45ded7a2 100644 --- a/system/Cache/Handlers/WincacheHandler.php +++ b/system/Cache/Handlers/WincacheHandler.php @@ -73,6 +73,8 @@ public function delete(string $key) /** * {@inheritDoc} + * + * @return never */ public function deleteMatching(string $pattern) { diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index cc6d3f67de0a..6ef3ab8261b2 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -52,7 +52,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.4.0'; + public const CI_VERSION = '4.4.1'; /** * App startup time. @@ -468,6 +468,8 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache // If a ResponseInterface instance is returned then send it back to the client and stop if ($possibleResponse instanceof ResponseInterface) { + $this->outputBufferingEnd(); + return $possibleResponse; } @@ -701,6 +703,8 @@ public function displayCache(Cache $config) * Tells the app that the final output should be cached. * * @deprecated 4.4.0 Moved to ResponseCache::setTtl(). to No longer used. + * + * @return void */ public static function cache(int $time) { diff --git a/system/Common.php b/system/Common.php index ff9ca929c9f9..c964e10ea899 100644 --- a/system/Common.php +++ b/system/Common.php @@ -886,11 +886,21 @@ function redirect(?string $route = null): RedirectResponse /** * Generates the solidus character (`/`) depending on the HTML5 compatibility flag in `Config\DocTypes` * + * @param DocTypes|null $docTypesConfig New config. For testing purpose only. + * * @internal */ - function _solidus(): string + function _solidus(?DocTypes $docTypesConfig = null): string { - if (config(DocTypes::class)->html5 ?? false) { + static $docTypes = null; + + if ($docTypesConfig !== null) { + $docTypes = $docTypesConfig; + } + + $docTypes ??= new DocTypes(); + + if ($docTypes->html5 ?? false) { return ''; } diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 0eb6d2443e44..6db25549dca9 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -118,6 +118,7 @@ public static function define(string $component, string $alias, string $classnam self::getOptions($component); self::$aliases[$component][$alias] = $classname; + self::$updated[$component] = true; } /** @@ -142,6 +143,7 @@ public static function __callStatic(string $component, array $arguments) return new $class(...$arguments); } + // Try to locate the class if ($class = self::locateClass($options, $alias)) { return new $class(...$arguments); } @@ -160,14 +162,8 @@ public static function __callStatic(string $component, array $arguments) return null; } - self::$instances[$options['component']][$class] = new $class(...$arguments); - self::$aliases[$options['component']][$alias] = $class; - self::$updated[$options['component']] = true; - - // If a short classname is specified, also register FQCN to share the instance. - if (! isset(self::$aliases[$options['component']][$class])) { - self::$aliases[$options['component']][$class] = $class; - } + self::createInstance($options['component'], $class, $arguments); + self::setAlias($options['component'], $alias, $class); return self::$instances[$options['component']][$class]; } @@ -179,6 +175,7 @@ public static function __callStatic(string $component, array $arguments) */ private static function getDefinedInstance(array $options, string $alias, array $arguments) { + // The alias is already defined. if (isset(self::$aliases[$options['component']][$alias])) { $class = self::$aliases[$options['component']][$alias]; @@ -189,15 +186,50 @@ private static function getDefinedInstance(array $options, string $alias, array return self::$instances[$options['component']][$class]; } - self::$instances[$options['component']][$class] = new $class(...$arguments); + self::createInstance($options['component'], $class, $arguments); return self::$instances[$options['component']][$class]; } } + // Try to locate the class + if (! $class = self::locateClass($options, $alias)) { + return null; + } + + // Check for an existing instance for the class + if (isset(self::$instances[$options['component']][$class])) { + self::setAlias($options['component'], $alias, $class); + + return self::$instances[$options['component']][$class]; + } + return null; } + /** + * Creates the shared instance. + */ + private static function createInstance(string $component, string $class, array $arguments): void + { + self::$instances[$component][$class] = new $class(...$arguments); + self::$updated[$component] = true; + } + + /** + * Sets alias + */ + private static function setAlias(string $component, string $alias, string $class): void + { + self::$aliases[$component][$alias] = $class; + self::$updated[$component] = true; + + // If a short classname is specified, also register FQCN to share the instance. + if (! isset(self::$aliases[$component][$class]) && ! self::isNamespaced($alias)) { + self::$aliases[$component][$class] = $class; + } + } + /** * Is the component Config? * diff --git a/system/Config/Services.php b/system/Config/Services.php index 2a4596bf203c..720bb3c487ce 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -781,7 +781,7 @@ public static function toolbar(?ToolbarConfig $config = null, bool $getShared = /** * The URI class provides a way to model and manipulate URIs. * - * @param string $uri + * @param string|null $uri The URI string * * @return URI The current URI if $uri is null. */ diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 07486cffaeec..302020323577 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -706,7 +706,6 @@ public function where($key, $value = null, ?bool $escape = null) * * @param array|RawSql|string $key * @param mixed $value - * @param bool $escape * * @return $this */ diff --git a/system/Database/Migration.php b/system/Database/Migration.php index 0f1ab2c74f2f..1616830f9267 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -41,8 +41,6 @@ abstract class Migration /** * Constructor. - * - * @param Forge $forge */ public function __construct(?Forge $forge = null) { @@ -53,8 +51,6 @@ public function __construct(?Forge $forge = null) /** * Returns the database group name this migration uses. - * - * @return string */ public function getDBGroup(): ?string { diff --git a/system/Database/Query.php b/system/Database/Query.php index 702575ca857c..a78190a21798 100644 --- a/system/Database/Query.php +++ b/system/Database/Query.php @@ -167,8 +167,6 @@ public function getQuery(): string * for it's start and end values. If no end value is present, will * use the current time to determine total duration. * - * @param float $end - * * @return $this */ public function setDuration(float $start, ?float $end = null) diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index d20125b9b5bd..19a67b455db3 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -346,9 +346,6 @@ protected function roundTo(float $number, int $increments = 5): float /** * Prepare for debugging.. * - * @param RequestInterface $request - * @param ResponseInterface $response - * * @return void */ public function prepare(?RequestInterface $request = null, ?ResponseInterface $response = null) diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index 38bab087ae81..8a302b4019e9 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -140,6 +140,14 @@ white-space: nowrap; z-index: 10000; } +#debug-bar .toolbar .rotate { + animation: toolbar-rotate 9s linear infinite; +} +@keyframes toolbar-rotate { + to { + transform: rotate(360deg); + } +} #debug-bar.fixed-top { bottom: auto; top: 0; @@ -796,14 +804,3 @@ .debug-bar-noverflow { overflow: hidden; } - -/* ENDLESS ROTATE */ -.rotate { - animation: rotate 9s linear infinite; -} - -@keyframes rotate { - to { - transform: rotate(360deg); - } -} diff --git a/system/Email/Email.php b/system/Email/Email.php index d962042d5e7a..b2f6874c32c9 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -113,7 +113,9 @@ class Email /** * SMTP Encryption * - * @var string Empty, 'tls' or 'ssl' + * @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command + * to the server. 'ssl' means implicit SSL. Connection on port + * 465 should set this to ''. */ public $SMTPCrypto = ''; @@ -1868,9 +1870,13 @@ protected function SMTPConnect() $ssl = ''; + // Connection to port 465 should use implicit TLS (without STARTTLS) + // as per RFC 8314. if ($this->SMTPPort === 465) { $ssl = 'tls://'; - } elseif ($this->SMTPCrypto === 'ssl') { + } + // But if $SMTPCrypto is set to `ssl`, SSL can be used. + if ($this->SMTPCrypto === 'ssl') { $ssl = 'ssl://'; } diff --git a/system/Encryption/Exceptions/EncryptionException.php b/system/Encryption/Exceptions/EncryptionException.php index d226fd5dd08b..41a96b69e34a 100644 --- a/system/Encryption/Exceptions/EncryptionException.php +++ b/system/Encryption/Exceptions/EncryptionException.php @@ -45,8 +45,6 @@ public static function forNoHandlerAvailable(string $handler) /** * Thrown when the handler requested is unknown. * - * @param string $driver - * * @return static */ public static function forUnKnownHandler(?string $driver = null) diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index 0481cc35823f..dd4f54c928bf 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -136,7 +136,7 @@ public function __construct(?array $data = null) * properties, using any `setCamelCasedProperty()` methods * that may or may not exist. * - * @param array $data + * @param array $data * * @return $this */ diff --git a/system/Format/Exceptions/FormatException.php b/system/Format/Exceptions/FormatException.php index cfe39d3b8307..55c36d3d23aa 100644 --- a/system/Format/Exceptions/FormatException.php +++ b/system/Format/Exceptions/FormatException.php @@ -36,7 +36,7 @@ public static function forInvalidFormatter(string $class) * Thrown in JSONFormatter when the json_encode produces * an error code other than JSON_ERROR_NONE and JSON_ERROR_RECURSION. * - * @param string $error + * @param string $error The error message * * @return static */ diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index fb9d780c1485..a149c4102f34 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -778,8 +778,6 @@ public function setHost(string $str) /** * Sets the port portion of the URI. * - * @param int $port - * * @return $this * * @TODO PSR-7: Should be `withPort($port)`. diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 1ccc2109c119..02797b2d20c3 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -112,8 +112,6 @@ public function __construct(?UserAgents $config = null) /** * Is Browser - * - * @param string $key */ public function isBrowser(?string $key = null): bool { @@ -132,8 +130,6 @@ public function isBrowser(?string $key = null): bool /** * Is Robot - * - * @param string $key */ public function isRobot(?string $key = null): bool { @@ -152,8 +148,6 @@ public function isRobot(?string $key = null): bool /** * Is Mobile - * - * @param string $key */ public function isMobile(?string $key = null): bool { diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 134c7c6ce1cd..cf23fd0fcd23 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -549,8 +549,6 @@ public function getEXIF(?string $key = null, bool $silent = false) * - bottom * - bottom-right * - * @param int $height - * * @return BaseHandler */ public function fit(int $width, ?int $height = null, string $position = 'center') diff --git a/system/Images/ImageHandlerInterface.php b/system/Images/ImageHandlerInterface.php index 2b924720d2dd..1c3593a99bd4 100644 --- a/system/Images/ImageHandlerInterface.php +++ b/system/Images/ImageHandlerInterface.php @@ -132,7 +132,7 @@ public function text(string $text, array $options = []); * $image->resize(100, 200, true) * ->save($target); * - * @param string $target + * @param string|null $target The path to save the file to. * * @return bool */ diff --git a/system/Log/Handlers/ChromeLoggerHandler.php b/system/Log/Handlers/ChromeLoggerHandler.php index 9ce30a822364..3354e5fb2db8 100644 --- a/system/Log/Handlers/ChromeLoggerHandler.php +++ b/system/Log/Handlers/ChromeLoggerHandler.php @@ -149,8 +149,6 @@ protected function format($object) /** * Attaches the header and the content to the passed in request object. * - * @param ResponseInterface $response - * * @return void */ public function sendLogs(?ResponseInterface &$response = null) diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index 2c666e2ce579..3a9f5a772f0a 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -387,8 +387,6 @@ public function only(array $queries): self /** * Ensures that an array exists for the group specified. * - * @param int $perPage - * * @return void */ protected function ensureGroup(string $group, ?int $perPage = null) diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 82c6a6dd5bbe..8383280114b5 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -28,7 +28,9 @@ class RouteCollection implements RouteCollectionInterface { /** * The namespace to be added to any Controllers. - * Defaults to the global namespaces (\) + * Defaults to the global namespaces (\). + * + * This must have a trailing backslash (\). * * @var string */ @@ -288,7 +290,7 @@ public function __construct(FileLocator $locator, Modules $moduleConfig, Routing $this->httpHost = Services::request()->getServer('HTTP_HOST'); // Setup based on config file. Let routes file override. - $this->defaultNamespace = $routing->defaultNamespace; + $this->defaultNamespace = rtrim($routing->defaultNamespace, '\\') . '\\'; $this->defaultController = $routing->defaultController; $this->defaultMethod = $routing->defaultMethod; $this->translateURIDashes = $routing->translateURIDashes; @@ -1149,11 +1151,13 @@ public function environment(string $env, Closure $callback): RouteCollectionInte public function reverseRoute(string $search, ...$params) { // Named routes get higher priority. - foreach ($this->routesNames as $collection) { + foreach ($this->routesNames as $verb => $collection) { if (array_key_exists($search, $collection)) { $routeKey = $collection[$search]; - return $this->buildReverseRoute($routeKey, $params); + $from = $this->routes[$verb][$routeKey]['from']; + + return $this->buildReverseRoute($from, $params); } } @@ -1169,8 +1173,9 @@ public function reverseRoute(string $search, ...$params) // If it's not a named route, then loop over // all routes to find a match. foreach ($this->routes as $collection) { - foreach ($collection as $routeKey => $route) { - $to = $route['handler']; + foreach ($collection as $route) { + $to = $route['handler']; + $from = $route['from']; // ignore closures if (! is_string($to)) { @@ -1194,7 +1199,7 @@ public function reverseRoute(string $search, ...$params) continue; } - return $this->buildReverseRoute($routeKey, $params); + return $this->buildReverseRoute($from, $params); } } @@ -1309,21 +1314,21 @@ protected function fillRouteParams(string $from, ?array $params = null): string * @param array $params One or more parameters to be passed to the route. * The last parameter allows you to set the locale. */ - protected function buildReverseRoute(string $routeKey, array $params): string + protected function buildReverseRoute(string $from, array $params): string { $locale = null; // Find all of our back-references in the original route - preg_match_all('/\(([^)]+)\)/', $routeKey, $matches); + preg_match_all('/\(([^)]+)\)/', $from, $matches); if (empty($matches[0])) { - if (strpos($routeKey, '{locale}') !== false) { + if (strpos($from, '{locale}') !== false) { $locale = $params[0] ?? null; } - $routeKey = $this->replaceLocale($routeKey, $locale); + $from = $this->replaceLocale($from, $locale); - return '/' . ltrim($routeKey, '/'); + return '/' . ltrim($from, '/'); } // Locale is passed? @@ -1334,25 +1339,31 @@ protected function buildReverseRoute(string $routeKey, array $params): string // Build our resulting string, inserting the $params in // the appropriate places. - foreach ($matches[0] as $index => $pattern) { + foreach ($matches[0] as $index => $placeholder) { if (! isset($params[$index])) { throw new InvalidArgumentException( - 'Missing argument for "' . $pattern . '" in route "' . $routeKey . '".' + 'Missing argument for "' . $placeholder . '" in route "' . $from . '".' ); } + + // Remove `(:` and `)` when $placeholder is a placeholder. + $placeholderName = substr($placeholder, 2, -1); + // or maybe $placeholder is not a placeholder, but a regex. + $pattern = $this->placeholders[$placeholderName] ?? $placeholder; + if (! preg_match('#^' . $pattern . '$#u', $params[$index])) { throw RouterException::forInvalidParameterType(); } // Ensure that the param we're inserting matches // the expected param type. - $pos = strpos($routeKey, $pattern); - $routeKey = substr_replace($routeKey, $params[$index], $pos, strlen($pattern)); + $pos = strpos($from, $placeholder); + $from = substr_replace($from, $params[$index], $pos, strlen($placeholder)); } - $routeKey = $this->replaceLocale($routeKey, $locale); + $from = $this->replaceLocale($from, $locale); - return '/' . ltrim($routeKey, '/'); + return '/' . ltrim($from, '/'); } /** diff --git a/system/Router/RouteCollectionInterface.php b/system/Router/RouteCollectionInterface.php index 0ca43523a89b..00f1d4c8bdc2 100644 --- a/system/Router/RouteCollectionInterface.php +++ b/system/Router/RouteCollectionInterface.php @@ -28,8 +28,9 @@ interface RouteCollectionInterface /** * Adds a single route to the collection. * - * @param array|Closure|string $to - * @param array $options + * @param string $from The route path (with placeholders or regex) + * @param array|Closure|string $to The route handler + * @param array|null $options The route options * * @return RouteCollectionInterface */ @@ -44,7 +45,7 @@ public function add(string $from, $to, ?array $options = null); * multiple placeholders added at once. * * @param array|string $placeholder - * @param string $pattern + * @param string|null $pattern The regex pattern * * @return RouteCollectionInterface */ diff --git a/system/Test/DOMParser.php b/system/Test/DOMParser.php index 5ab12e44ab29..54762643e38d 100644 --- a/system/Test/DOMParser.php +++ b/system/Test/DOMParser.php @@ -101,9 +101,6 @@ public function withFile(string $path) /** * Checks to see if the text is found within the result. - * - * @param string $search - * @param string $element */ public function see(?string $search = null, ?string $element = null): bool { @@ -121,8 +118,6 @@ public function see(?string $search = null, ?string $element = null): bool /** * Checks to see if the text is NOT found within the result. - * - * @param string $search */ public function dontSee(?string $search = null, ?string $element = null): bool { diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index 371e302c59ae..02835391ca48 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -51,8 +51,6 @@ class FeatureTestCase extends CIUnitTestCase * ['get', 'home', 'Home::index'] * ] * - * @param array $routes - * * @return $this */ protected function withRoutes(?array $routes = null) diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index b117a1d890ae..23e56990b45b 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -39,7 +39,7 @@ trait FeatureTestTrait * ['get', 'home', 'Home::index'] * ] * - * @param array $routes + * @param array|null $routes Array to set routes * * @return $this */ @@ -51,7 +51,11 @@ protected function withRoutes(?array $routes = null) $collection->resetRoutes(); foreach ($routes as $route) { - $collection->{$route[0]}($route[1], $route[2]); + if (isset($route[3])) { + $collection->{$route[0]}($route[1], $route[2], $route[3]); + } else { + $collection->{$route[0]}($route[1], $route[2]); + } } } @@ -292,6 +296,9 @@ protected function setupRequest(string $method, ?string $path = null): IncomingR if ($config->forceGlobalSecureRequests) { $_SERVER['HTTPS'] = 'test'; + $server = $request->getServer(); + $server['HTTPS'] = 'test'; + $request->setGlobal('server', $server); } return $request; diff --git a/system/Typography/Typography.php b/system/Typography/Typography.php index 1ca7a56ff598..fb05b68faefe 100644 --- a/system/Typography/Typography.php +++ b/system/Typography/Typography.php @@ -323,10 +323,11 @@ protected function protectCharacters(array $match): string */ public function nl2brExceptPre(string $str): string { - $newstr = ''; + $newstr = ''; + $docTypes = new DocTypes(); for ($ex = explode('pre>', $str), $ct = count($ex), $i = 0; $i < $ct; $i++) { - $xhtml = ! (config(DocTypes::class)->html5 ?? false); + $xhtml = ! ($docTypes->html5 ?? false); $newstr .= (($i % 2) === 0) ? nl2br($ex[$i], $xhtml) : $ex[$i]; if ($ct - 1 !== $i) { diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index 9e18f4ef5cb9..8b0e3842a128 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -236,8 +236,6 @@ public function valid_cc_number(?string $ccNumber, string $type): bool /** * Checks the given number to see if the number passing a Luhn check. - * - * @param string $number */ protected function isValidLuhn(?string $number = null): bool { diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index bc4301c2a086..485b9efec2df 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -176,8 +176,6 @@ public function regex_match(?string $str, string $pattern): bool * timezone_identifiers_list function. * * @see http://php.net/manual/en/datetimezone.listidentifiers.php - * - * @param string $str */ public function timezone(?string $str = null): bool { @@ -189,8 +187,6 @@ public function timezone(?string $str = null): bool * * Tests a string for characters outside of the Base64 alphabet * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 - * - * @param string $str */ public function valid_base64(?string $str = null): bool { @@ -203,8 +199,6 @@ public function valid_base64(?string $str = null): bool /** * Valid JSON - * - * @param string $str */ public function valid_json(?string $str = null): bool { @@ -215,8 +209,6 @@ public function valid_json(?string $str = null): bool /** * Checks for a correctly formatted email address - * - * @param string $str */ public function valid_email(?string $str = null): bool { @@ -233,8 +225,6 @@ public function valid_email(?string $str = null): bool * * Example: * valid_emails[one@example.com,two@example.com] - * - * @param string $str */ public function valid_emails(?string $str = null): bool { diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 1bb2c243ebf5..e85b7443088e 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -204,8 +204,6 @@ public function min_length(?string $str, string $val): bool /** * Does not equal the static value provided. - * - * @param string $str */ public function not_equals(?string $str, string $val): bool { @@ -214,8 +212,6 @@ public function not_equals(?string $str, string $val): bool /** * Value should not be within an array of values. - * - * @param string $value */ public function not_in_list(?string $value, string $list): bool { diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index 48936397449e..5cb70bef1676 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -11,4 +11,8 @@ namespace Tests\Support\Config\Filters; -$filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; +/** + * @psalm-suppress UndefinedGlobalVariable + */ +$filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; +$filters->aliases['test-redirectfilter'] = \Tests\Support\Filters\RedirectFilter::class; diff --git a/tests/_support/Config/Services.php b/tests/_support/Config/Services.php index e051ea8d6b24..379211712af9 100644 --- a/tests/_support/Config/Services.php +++ b/tests/_support/Config/Services.php @@ -28,7 +28,7 @@ class Services extends BaseServices /** * The URI class provides a way to model and manipulate URIs. * - * @param string $uri + * @param string|null $uri The URI string * * @return URI */ diff --git a/tests/_support/Filters/RedirectFilter.php b/tests/_support/Filters/RedirectFilter.php new file mode 100644 index 000000000000..b04b0ea18706 --- /dev/null +++ b/tests/_support/Filters/RedirectFilter.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Filters; + +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +class RedirectFilter implements \CodeIgniter\Filters\FilterInterface +{ + public function before(RequestInterface $request, $arguments = null) + { + return redirect()->to('login'); + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void + { + } +} diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php index c678a0dd1fbb..b6702c5a7376 100644 --- a/tests/system/CommonFunctionsTest.php +++ b/tests/system/CommonFunctionsTest.php @@ -31,6 +31,7 @@ use CodeIgniter\Test\TestLogger; use Config\App; use Config\Cookie; +use Config\DocTypes; use Config\Logger; use Config\Modules; use Config\Routing; @@ -180,14 +181,24 @@ public function testSolidusElement(): void public function testSolidusElementXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $this->assertSame(' /', _solidus()); - // Reset - $doctypes->html5 = $default; + $this->enableHtml5(); + } + + private function disableHtml5() + { + $doctypes = new DocTypes(); + $doctypes->html5 = false; + _solidus($doctypes); + } + + private function enableHtml5() + { + $doctypes = new DocTypes(); + _solidus($doctypes); } public function testView(): void diff --git a/tests/system/Config/FactoriesTest.php b/tests/system/Config/FactoriesTest.php index c5936a7c341f..b53c1e9133e5 100644 --- a/tests/system/Config/FactoriesTest.php +++ b/tests/system/Config/FactoriesTest.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Config; use CodeIgniter\Test\CIUnitTestCase; +use Config\App; use Config\Database; use InvalidArgumentException; use ReflectionClass; @@ -322,6 +323,14 @@ public function testCanLoadTwoCellsWithSameShortName() $this->assertNotSame($cell1, $cell2); } + public function testCanLoadSharedConfigWithDifferentAlias() + { + $config1 = Factories::config(App::class); + $config2 = Factories::config('App'); + + $this->assertSame($config1, $config2); + } + public function testDefineSameAliasTwiceWithDifferentClasses() { $this->expectException(InvalidArgumentException::class); diff --git a/tests/system/Helpers/FormHelperTest.php b/tests/system/Helpers/FormHelperTest.php index 8ed1bdeb7201..3b2482513581 100644 --- a/tests/system/Helpers/FormHelperTest.php +++ b/tests/system/Helpers/FormHelperTest.php @@ -14,6 +14,7 @@ use CodeIgniter\HTTP\SiteURI; use CodeIgniter\Test\CIUnitTestCase; use Config\App; +use Config\DocTypes; use Config\Filters; use Config\Services; @@ -262,9 +263,7 @@ public function testFormInput(): void public function testFormInputXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $expected = <<\n @@ -279,8 +278,20 @@ public function testFormInputXHTML(): void ]; $this->assertSame($expected, form_input($data)); - // Reset - $doctypes->html5 = $default; + $this->enableHtml5(); + } + + private function disableHtml5() + { + $doctypes = new DocTypes(); + $doctypes->html5 = false; + _solidus($doctypes); + } + + private function enableHtml5() + { + $doctypes = new DocTypes(); + _solidus($doctypes); } public function testFormInputWithExtra(): void @@ -317,17 +328,14 @@ public function testFormUpload(): void public function testFormUploadXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $expected = <<\n EOH; $this->assertSame($expected, form_upload('attachment')); - // Reset - $doctypes->html5 = $default; + $this->enableHtml5(); } public function testFormTextarea(): void @@ -656,17 +664,14 @@ public function testFormCheckbox(): void public function testFormCheckboxXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $expected = <<\n EOH; $this->assertSame($expected, form_checkbox('newsletter', 'accept', true)); - // Reset - $doctypes->html5 = $default; + $this->enableHtml5(); } public function testFormCheckboxArrayData(): void diff --git a/tests/system/Helpers/HTMLHelperTest.php b/tests/system/Helpers/HTMLHelperTest.php index 0d9452640730..f591b841024d 100755 --- a/tests/system/Helpers/HTMLHelperTest.php +++ b/tests/system/Helpers/HTMLHelperTest.php @@ -15,6 +15,7 @@ use CodeIgniter\Files\Exceptions\FileNotFoundException; use CodeIgniter\Test\CIUnitTestCase; use Config\App; +use Config\DocTypes; /** * @internal @@ -206,41 +207,48 @@ public function testIMGWithIndexpage(): void public function testIMGXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $target = 'http://site.com/images/picture.jpg'; $expected = ''; $this->assertSame($expected, img($target)); - $doctypes->html5 = $default; + $this->enableHtml5(); } - public function testIMGXHTMLWithoutProtocol(): void + private function disableHtml5() { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; + $doctypes = new DocTypes(); $doctypes->html5 = false; + _solidus($doctypes); + } + + private function enableHtml5() + { + $doctypes = new DocTypes(); + _solidus($doctypes); + } + + public function testIMGXHTMLWithoutProtocol(): void + { + $this->disableHtml5(); $target = 'assets/mugshot.jpg'; $expected = ''; $this->assertSame($expected, img($target)); - $doctypes->html5 = $default; + $this->enableHtml5(); } public function testIMGXHTMLWithIndexpage(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $target = 'assets/mugshot.jpg'; $expected = ''; $this->assertSame($expected, img($target, true)); - $doctypes->html5 = $default; + $this->enableHtml5(); } public function testImgData(): void @@ -355,16 +363,13 @@ public function testLinkTag(): void public function testLinkTagXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $target = 'css/mystyles.css'; $expected = ''; $this->assertSame($expected, link_tag($target)); - // Reset - $doctypes->html5 = $default; + $this->enableHtml5(); } public function testLinkTagMedia(): void @@ -509,9 +514,7 @@ public function testVideoWithTracks(): void public function testVideoWithTracksXHTML(): void { - $doctypes = config('DocTypes'); - $default = $doctypes->html5; - $doctypes->html5 = false; + $this->disableHtml5(); $expected = <<<'EOH'