From 81cead15a32f09fcb45ecd811f66114e5f12e6ca Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 25 Aug 2023 15:35:29 +0900 Subject: [PATCH 01/36] docs: update RELEASE.md --- admin/RELEASE.md | 5 +++++ 1 file changed, 5 insertions(+) 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 From a2e8e5cbc415573aa286f0102ee4625e7d6bda8e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 25 Aug 2023 15:48:10 +0800 Subject: [PATCH 02/36] fix: add types for cache handlers --- phpstan-baseline.php | 55 ---------------------- system/Cache/Handlers/BaseHandler.php | 3 ++ system/Cache/Handlers/DummyHandler.php | 2 + system/Cache/Handlers/FileHandler.php | 2 + system/Cache/Handlers/MemcachedHandler.php | 2 + system/Cache/Handlers/PredisHandler.php | 2 + system/Cache/Handlers/RedisHandler.php | 2 + system/Cache/Handlers/WincacheHandler.php | 2 + system/CodeIgniter.php | 2 + 9 files changed, 17 insertions(+), 55 deletions(-) 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/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..8308dab116cb 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -701,6 +701,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) { From 7e11339ed67673b2838e4f49402224b022e91e26 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 26 Aug 2023 05:51:31 +0900 Subject: [PATCH 03/36] docs: add missing Mandatory File Changes for Hot Reload --- .../source/installation/upgrade_440.rst | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/user_guide_src/source/installation/upgrade_440.rst b/user_guide_src/source/installation/upgrade_440.rst index 0fb292666020..9845360a674e 100644 --- a/user_guide_src/source/installation/upgrade_440.rst +++ b/user_guide_src/source/installation/upgrade_440.rst @@ -176,6 +176,76 @@ So you need to do: 2. Remove all settings in **app/Config/Routes.php** that are no longer needed. 3. If you use the environment-specific routes files, add them to the ``$routeFiles`` property in **app/Config/Routing.php**. +app/Config/Toolbar.php +---------------------- + +You need to add the new properties ``$watchedDirectories`` and ``$watchedExtensions`` +for :ref:`debug-toolbar-hot-reload`:: + + --- a/app/Config/Toolbar.php + +++ b/app/Config/Toolbar.php + @@ -88,4 +88,31 @@ class Toolbar extends BaseConfig + * `$maxQueries` defines the maximum amount of queries that will be stored. + */ + public int $maxQueries = 100; + + + + /** + + * -------------------------------------------------------------------------- + + * Watched Directories + + * -------------------------------------------------------------------------- + + * + + * Contains an array of directories that will be watched for changes and + + * used to determine if the hot-reload feature should reload the page or not. + + * We restrict the values to keep performance as high as possible. + + * + + * NOTE: The ROOTPATH will be prepended to all values. + + */ + + public array $watchedDirectories = [ + + 'app', + + ]; + + + + /** + + * -------------------------------------------------------------------------- + + * Watched File Extensions + + * -------------------------------------------------------------------------- + + * + + * Contains an array of file extensions that will be watched for changes and + + * used to determine if the hot-reload feature should reload the page or not. + + */ + + public array $watchedExtensions = [ + + 'php', 'css', 'js', 'html', 'svg', 'json', 'env', + + ]; + } + + +app/Config/Events.php +--------------------- + +You need to add the code to add a route for :ref:`debug-toolbar-hot-reload`:: + + --- a/app/Config/Events.php + +++ b/app/Config/Events.php + @@ -4,6 +4,7 @@ namespace Config; + + use CodeIgniter\Events\Events; + use CodeIgniter\Exceptions\FrameworkException; + +use CodeIgniter\HotReloader\HotReloader; + + /* + * -------------------------------------------------------------------- + @@ -44,5 +45,11 @@ Events::on('pre_system', static function () { + if (CI_DEBUG && ! is_cli()) { + Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); + Services::toolbar()->respond(); + + // Hot Reload route - for framework use on the hot reloader. + + if (ENVIRONMENT === 'development') { + + Services::routes()->get('__hot-reload', static function () { + + (new HotReloader())->run(); + + }); + + } + } + }); + app/Config/Cookie.php --------------------- From c34ae214dde16153ccb183d67c20a8b528164e18 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 26 Aug 2023 05:51:58 +0900 Subject: [PATCH 04/36] docs: remove `-` for listing Only one item is there. --- user_guide_src/source/installation/upgrade_440.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/installation/upgrade_440.rst b/user_guide_src/source/installation/upgrade_440.rst index 9845360a674e..76bcc2957a68 100644 --- a/user_guide_src/source/installation/upgrade_440.rst +++ b/user_guide_src/source/installation/upgrade_440.rst @@ -155,8 +155,8 @@ Config Files app/Config/App.php ------------------ -- The property ``$proxyIPs`` must be an array. If you don't use proxy servers, - it must be ``public array $proxyIPs = [];``. +The property ``$proxyIPs`` must be an array. If you don't use proxy servers, +it must be ``public array $proxyIPs = [];``. .. _upgrade-440-config-routing: From 4bb67f2124568ccfcf04892b55c4862f31f63d52 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 26 Aug 2023 10:09:12 +0900 Subject: [PATCH 05/36] docs: changelog and upgrade for 4.4.1 --- user_guide_src/source/changelogs/index.rst | 1 + user_guide_src/source/changelogs/v4.4.1.rst | 29 +++++++++++ .../source/installation/upgrade_441.rst | 50 +++++++++++++++++++ .../source/installation/upgrading.rst | 1 + 4 files changed, 81 insertions(+) create mode 100644 user_guide_src/source/changelogs/v4.4.1.rst create mode 100644 user_guide_src/source/installation/upgrade_441.rst diff --git a/user_guide_src/source/changelogs/index.rst b/user_guide_src/source/changelogs/index.rst index 2a04069e2406..dd0ac6db1034 100644 --- a/user_guide_src/source/changelogs/index.rst +++ b/user_guide_src/source/changelogs/index.rst @@ -12,6 +12,7 @@ See all the changes. .. toctree:: :titlesonly: + v4.4.1 v4.4.0 v4.3.8 v4.3.7 diff --git a/user_guide_src/source/changelogs/v4.4.1.rst b/user_guide_src/source/changelogs/v4.4.1.rst new file mode 100644 index 000000000000..b3d229aa210c --- /dev/null +++ b/user_guide_src/source/changelogs/v4.4.1.rst @@ -0,0 +1,29 @@ +Version 4.4.1 +############# + +Release Date: Unreleased + +**4.4.1 release of CodeIgniter4** + +.. contents:: + :local: + :depth: 3 + +BREAKING +******** + +Message Changes +*************** + +Changes +******* + +Deprecations +************ + +Bugs Fixed +********** + +See the repo's +`CHANGELOG.md `_ +for a complete list of bugs fixed. diff --git a/user_guide_src/source/installation/upgrade_441.rst b/user_guide_src/source/installation/upgrade_441.rst new file mode 100644 index 000000000000..9824405edb37 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_441.rst @@ -0,0 +1,50 @@ +############################# +Upgrading from 4.4.0 to 4.4.1 +############################# + +Please refer to the upgrade instructions corresponding to your installation method. + +- :ref:`Composer Installation App Starter Upgrading ` +- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading ` +- :ref:`Manual Installation Upgrading ` + +.. contents:: + :local: + :depth: 2 + +Mandatory File Changes +********************** + +Breaking Changes +**************** + +Breaking Enhancements +********************* + +Project Files +************* + +Some files in the **project space** (root, app, public, writable) received updates. Due to +these files being outside of the **system** scope they will not be changed without your intervention. + +There are some third-party CodeIgniter modules available to assist with merging changes to +the project space: `Explore on Packagist `_. + +Content Changes +=============== + +The following files received significant changes (including deprecations or visual adjustments) +and it is recommended that you merge the updated versions with your application: + +Config +------ + +- @TODO + +All Changes +=========== + +This is a list of all files in the **project space** that received changes; +many will be simple comments or formatting that have no effect on the runtime: + +- @TODO diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index c675327d7412..210b1499c8bb 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -16,6 +16,7 @@ See also :doc:`./backward_compatibility_notes`. backward_compatibility_notes + upgrade_441 upgrade_440 upgrade_438 upgrade_437 From 9dd7cb040a46ef522ac53989213f6c4656396dde Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 09:31:47 +0900 Subject: [PATCH 06/36] test: add test when filter returns response --- system/Test/FeatureTestTrait.php | 6 ++++- tests/_support/Config/Filters.php | 3 ++- tests/_support/Filters/RedirectFilter.php | 27 ++++++++++++++++++++++ tests/system/Test/FeatureTestTraitTest.php | 15 ++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/_support/Filters/RedirectFilter.php diff --git a/system/Test/FeatureTestTrait.php b/system/Test/FeatureTestTrait.php index b117a1d890ae..c59c801d4f4a 100644 --- a/system/Test/FeatureTestTrait.php +++ b/system/Test/FeatureTestTrait.php @@ -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]); + } } } diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index 48936397449e..dda1549a7154 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -11,4 +11,5 @@ namespace Tests\Support\Config\Filters; -$filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; +$filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; +$filters->aliases['test-redirectfilter'] = \Tests\Support\Filters\RedirectFilter::class; 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/Test/FeatureTestTraitTest.php b/tests/system/Test/FeatureTestTraitTest.php index 0f5bb7ce33d5..c1a341c0aa2b 100644 --- a/tests/system/Test/FeatureTestTraitTest.php +++ b/tests/system/Test/FeatureTestTraitTest.php @@ -77,6 +77,21 @@ public function testCallGetAndUriString(): void $this->assertSame('http://example.com/index.php/foo/bar/1/2/3', current_url()); } + public function testCallGetAndFilterReturnsResponse(): void + { + $this->withRoutes([ + [ + 'get', + 'admin', + static fn () => 'Admin Area', + ['filter' => 'test-redirectfilter'], + ], + ]); + $response = $this->get('admin'); + + $response->assertRedirectTo('login'); + } + public function testClosureWithEcho() { $this->withRoutes([ From e28b9d79c6f7b7804cef54a6c6aea8b10f9cf3ea Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 09:34:31 +0900 Subject: [PATCH 07/36] fix: add missing outputBufferingEnd() --- system/CodeIgniter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 8308dab116cb..e991af8de121 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -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; } From 1e91936c00dd73d5b260ce6718124dc2230e9ce8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 09:50:03 +0900 Subject: [PATCH 08/36] docs: suppress paslm error --- tests/_support/Config/Filters.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/_support/Config/Filters.php b/tests/_support/Config/Filters.php index dda1549a7154..5cb70bef1676 100644 --- a/tests/_support/Config/Filters.php +++ b/tests/_support/Config/Filters.php @@ -11,5 +11,8 @@ namespace Tests\Support\Config\Filters; +/** + * @psalm-suppress UndefinedGlobalVariable + */ $filters->aliases['test-customfilter'] = \Tests\Support\Filters\Customfilter::class; $filters->aliases['test-redirectfilter'] = \Tests\Support\Filters\RedirectFilter::class; From 936d298507087bd8c2bef7e6141034fb849a15c5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 10:19:14 +0900 Subject: [PATCH 09/36] test: add test for get same config with different alias If the request class is the same, it is better to return the shared instance. --- tests/system/Config/FactoriesTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) 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); From 42f90a2d09bf1aaf55ffe98a3c3f141989723cba Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 10:20:42 +0900 Subject: [PATCH 10/36] fix: Factories do not return shared instance If it is called by different aliase like `'App'` and `App::class`. But if they are exactly the same FQCN, the shared instance should be returned. --- system/Config/Factories.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 0eb6d2443e44..935c80bad445 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -142,6 +142,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); } @@ -179,6 +180,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]; @@ -195,6 +197,21 @@ private static function getDefinedInstance(array $options, string $alias, array } } + // Try to locate the class + if (! $class = self::locateClass($options, $alias)) { + return null; + } + + // Need to verify if the shared instance matches the request + if (self::verifyInstanceOf($options, $class)) { + // Check for an existing instance for the class + if (isset(self::$instances[$options['component']][$class])) { + self::$aliases[$options['component']][$alias] = $class; + + return self::$instances[$options['component']][$class]; + } + } + return null; } From 39243390d772643f8b17ecc53ef2b9abe8dd2734 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 10:34:28 +0900 Subject: [PATCH 11/36] fix: Config cache may not be updated --- system/Config/Factories.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 935c80bad445..23a95e13b85f 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; } /** @@ -192,6 +193,7 @@ private static function getDefinedInstance(array $options, string $alias, array } self::$instances[$options['component']][$class] = new $class(...$arguments); + self::$updated[$options['component']] = true; return self::$instances[$options['component']][$class]; } @@ -207,6 +209,7 @@ private static function getDefinedInstance(array $options, string $alias, array // Check for an existing instance for the class if (isset(self::$instances[$options['component']][$class])) { self::$aliases[$options['component']][$alias] = $class; + self::$updated[$options['component']] = true; return self::$instances[$options['component']][$class]; } From 65a138521c6d8de8dad19bb5f097430e97431d76 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 10:49:29 +0900 Subject: [PATCH 12/36] refactor: extract method --- system/Config/Factories.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 23a95e13b85f..4e0218a6e835 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -162,9 +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; + self::createInstance($options['component'], $class, $arguments); + self::$aliases[$options['component']][$alias] = $class; // If a short classname is specified, also register FQCN to share the instance. if (! isset(self::$aliases[$options['component']][$class])) { @@ -192,8 +191,7 @@ private static function getDefinedInstance(array $options, string $alias, array return self::$instances[$options['component']][$class]; } - self::$instances[$options['component']][$class] = new $class(...$arguments); - self::$updated[$options['component']] = true; + self::createInstance($options['component'], $class, $arguments); return self::$instances[$options['component']][$class]; } @@ -218,6 +216,15 @@ private static function getDefinedInstance(array $options, string $alias, array 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; + } + /** * Is the component Config? * From 896b04717586503a619785816e8dc0f4e9f83faf Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 11:05:20 +0900 Subject: [PATCH 13/36] refactor: extract method --- system/Config/Factories.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 4e0218a6e835..a160793d3742 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -163,11 +163,11 @@ public static function __callStatic(string $component, array $arguments) } self::createInstance($options['component'], $class, $arguments); - self::$aliases[$options['component']][$alias] = $class; + self::setAlias($options['component'], $alias, $class); // 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::setAlias($options['component'], $class, $class); } return self::$instances[$options['component']][$class]; @@ -206,8 +206,7 @@ private static function getDefinedInstance(array $options, string $alias, array if (self::verifyInstanceOf($options, $class)) { // Check for an existing instance for the class if (isset(self::$instances[$options['component']][$class])) { - self::$aliases[$options['component']][$alias] = $class; - self::$updated[$options['component']] = true; + self::setAlias($options['component'], $alias, $class); return self::$instances[$options['component']][$class]; } @@ -225,6 +224,15 @@ private static function createInstance(string $component, string $class, array $ 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; + } + /** * Is the component Config? * From a895f634b32dfb5b7b63f370c8927787af103e24 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 11:07:34 +0900 Subject: [PATCH 14/36] refactor: move logic --- system/Config/Factories.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index a160793d3742..2e24b5cef65c 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -165,11 +165,6 @@ public static function __callStatic(string $component, array $arguments) self::createInstance($options['component'], $class, $arguments); self::setAlias($options['component'], $alias, $class); - // If a short classname is specified, also register FQCN to share the instance. - if (! isset(self::$aliases[$options['component']][$class])) { - self::setAlias($options['component'], $class, $class); - } - return self::$instances[$options['component']][$class]; } @@ -231,6 +226,11 @@ private static function setAlias(string $component, string $alias, string $class { 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::$aliases[$component][$class] = $class; + } } /** From add2cbdbea526b97805d56209cad92b970d6f746 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 11:11:46 +0900 Subject: [PATCH 15/36] refactor: change if condition This is more intuitive. --- system/Config/Factories.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 2e24b5cef65c..ba49e9360a91 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -228,7 +228,7 @@ private static function setAlias(string $component, string $alias, string $class self::$updated[$component] = true; // If a short classname is specified, also register FQCN to share the instance. - if (! isset(self::$aliases[$component][$class])) { + if (! isset(self::$aliases[$component][$class]) && ! self::isNamespaced($alias)) { self::$aliases[$component][$class] = $class; } } From 1b8847914ae5252186544381ea6476368528c18f Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 11:19:30 +0900 Subject: [PATCH 16/36] refactor: remove unnecessary verifyInstanceOf() Because locateClass() already checks it. --- system/Config/Factories.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index ba49e9360a91..6db25549dca9 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -197,14 +197,11 @@ private static function getDefinedInstance(array $options, string $alias, array return null; } - // Need to verify if the shared instance matches the request - if (self::verifyInstanceOf($options, $class)) { - // Check for an existing instance for the class - if (isset(self::$instances[$options['component']][$class])) { - self::setAlias($options['component'], $alias, $class); + // 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 self::$instances[$options['component']][$class]; } return null; From b9efbfc9d5c2d44300161b94e959b0dffe39d681 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 27 Aug 2023 15:32:10 +0900 Subject: [PATCH 17/36] docs: add @immutable There is no reason to change the values after the instantiation. --- app/Config/Autoload.php | 2 ++ app/Config/DocTypes.php | 3 +++ app/Config/ForeignCharacters.php | 3 +++ app/Config/Mimes.php | 2 ++ app/Config/Modules.php | 2 ++ 5 files changed, 12 insertions(+) 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/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 { From aa93ae25ab047aed9c41b847e17388104d893656 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Aug 2023 05:45:17 +0900 Subject: [PATCH 18/36] test: add feature test for auto routing legacy --- tests/system/Test/FeatureTestTraitTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/system/Test/FeatureTestTraitTest.php b/tests/system/Test/FeatureTestTraitTest.php index 0f5bb7ce33d5..aee912281c76 100644 --- a/tests/system/Test/FeatureTestTraitTest.php +++ b/tests/system/Test/FeatureTestTraitTest.php @@ -11,9 +11,11 @@ namespace CodeIgniter\Test; +use CodeIgniter\Config\Factories; use CodeIgniter\Events\Events; use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\Response; +use Config\Routing; use Config\Services; /** @@ -616,4 +618,15 @@ public function testSetupRequestBodyWithBody(): void $this->assertSame('test', $request->getBody()); } + + public function testAutoRoutingLegacy() + { + $config = config(Routing::class); + $config->autoRoute = true; + Factories::injectMock('config', Routing::class, $config); + + $response = $this->get('home/index'); + + $response->assertOK(); + } } From 681f74d0059c6e819189c7a1bc8b038dc9af63c4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 28 Aug 2023 05:46:05 +0900 Subject: [PATCH 19/36] fix: auto routing legacy does not work at all --- system/Router/RouteCollection.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 82c6a6dd5bbe..36b8e4d52340 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; From 45709418db8067b699fbe93a058b72c2e9c27e22 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 29 Aug 2023 10:01:05 +0900 Subject: [PATCH 20/36] fix: replace `config(DocTypes::class)` with `new DocTypes()` --- system/Common.php | 3 ++- system/Typography/Typography.php | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/system/Common.php b/system/Common.php index ff9ca929c9f9..03f72640e92e 100644 --- a/system/Common.php +++ b/system/Common.php @@ -890,7 +890,8 @@ function redirect(?string $route = null): RedirectResponse */ function _solidus(): string { - if (config(DocTypes::class)->html5 ?? false) { + $docTypes = new DocTypes(); + if ($docTypes->html5 ?? false) { return ''; } 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) { From 3474bc51628915c5c86a3469d8c228073bd01153 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 29 Aug 2023 15:31:25 +0900 Subject: [PATCH 21/36] refactor: add way to chnage DocTypes for testing --- system/Common.php | 13 +++- tests/system/CommonFunctionsTest.php | 21 +++++-- tests/system/Helpers/FormHelperTest.php | 35 ++++++----- tests/system/Helpers/HTMLHelperTest.php | 81 +++++++++++-------------- 4 files changed, 81 insertions(+), 69 deletions(-) diff --git a/system/Common.php b/system/Common.php index 03f72640e92e..c964e10ea899 100644 --- a/system/Common.php +++ b/system/Common.php @@ -886,11 +886,20 @@ 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 { - $docTypes = new DocTypes(); + static $docTypes = null; + + if ($docTypesConfig !== null) { + $docTypes = $docTypesConfig; + } + + $docTypes ??= new DocTypes(); + if ($docTypes->html5 ?? false) { return ''; } 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/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'