From bd7b3b51622d934094adb156978939102f42c6d5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 23 Oct 2023 06:36:29 +0900 Subject: [PATCH 01/14] docs: improve description --- user_guide_src/source/models/model.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index 46bc5908cb41..880adfc0d9cb 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -69,7 +69,10 @@ Connecting to the Database ========================== When the class is first instantiated, if no database connection instance is passed to the constructor, -it will automatically connect to the default database group, as set in the configuration. You can +and if you don't set the ``$DBGroup`` property to your model class, +it will automatically connect to the default database group, as set in the database configuration. + +You can modify which group is used on a per-model basis by adding the ``$DBGroup`` property to your class. This ensures that within the model any references to ``$this->db`` are made through the appropriate connection. From cfe8b0986eac213508f45da346169933b45f8048 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 08:46:27 +0900 Subject: [PATCH 02/14] docs: add new section titles "Confirming Namespaces" and "Application Namespace" --- user_guide_src/source/concepts/autoloader.rst | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index 22722716661b..1a354208bf17 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -42,8 +42,10 @@ Configuration Initial configuration is done in **app/Config/Autoload.php**. This file contains two primary arrays: one for the classmap, and one for PSR-4 compatible namespaces. +.. _autoloader-namespaces: + Namespaces -********** +========== The recommended method for organizing your classes is to create one or more namespaces for your application's files. This is most important for any business-logic related classes, entity classes, @@ -55,11 +57,19 @@ those classes can be found in: The key of each row is the namespace itself. This does not need a trailing back slash. The value is the location to the directory the classes can be found in. -.. note:: You can check the namespace configuration by ``spark namespaces`` command: +.. _confirming-namespaces: + +Confirming Namespaces +===================== + +You can check the namespace configuration by ``spark namespaces`` command: + +.. code-block:: console - .. code-block:: console + php spark namespaces - php spark namespaces +Application Namespace +===================== By default, the application directory is namespace to the ``App`` namespace. You must namespace the controllers, libraries, or models in the application directory, and they will be found under the ``App`` namespace. @@ -77,7 +87,7 @@ You will need to modify any existing files that are referencing the current name namespace has changed. Classmap -******** +======== The classmap is used extensively by CodeIgniter to eke the last ounces of performance out of the system by not hitting the file-system with extra ``is_file()`` calls. You can use the classmap to link to From 1ab584536e4785e406d270d2a294e28ba470a55c Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 08:48:12 +0900 Subject: [PATCH 03/14] docs: add link to function page --- user_guide_src/source/general/modules.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/modules.rst b/user_guide_src/source/general/modules.rst index 2a6df4aa7b2e..c2173792ab4a 100644 --- a/user_guide_src/source/general/modules.rst +++ b/user_guide_src/source/general/modules.rst @@ -240,8 +240,9 @@ For Windows: Helpers ======= -Helpers will be automatically discovered within defined namespaces when using the ``helper()`` function, as long as it -is within the namespaces **Helpers** directory: +Helpers will be automatically discovered within defined namespaces when using the +:php:func:`helper()` function, as long as it is within the namespaces **Helpers** +directory: .. literalinclude:: modules/009.php From bb1369a231ff00d8f3fd8230b45519631f9fe9fc Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 08:48:32 +0900 Subject: [PATCH 04/14] docs: add link to "Helper Functions" --- user_guide_src/source/helpers/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/helpers/index.rst b/user_guide_src/source/helpers/index.rst index 7efa4cd069fb..82c8c992a0bf 100644 --- a/user_guide_src/source/helpers/index.rst +++ b/user_guide_src/source/helpers/index.rst @@ -3,6 +3,7 @@ Helpers ####### Helpers are collections of useful procedural functions. +See also :doc:`../general/helpers`. .. toctree:: :glob: From fa31844349cd568e290731fa0e6dfe7136ba5d35 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 08:49:19 +0900 Subject: [PATCH 05/14] docs: fix description for helper loading and add "Auto-Discovery and Composer Packages" --- user_guide_src/source/general/helpers.rst | 41 ++++++++++++++++------- user_guide_src/source/general/modules.rst | 2 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/user_guide_src/source/general/helpers.rst b/user_guide_src/source/general/helpers.rst index 6eadb9be6957..88accffd47b3 100644 --- a/user_guide_src/source/general/helpers.rst +++ b/user_guide_src/source/general/helpers.rst @@ -28,17 +28,17 @@ in your :doc:`controller <../incoming/controllers>` and :doc:`views <../outgoing/views>`. Helpers are typically stored in your **system/Helpers**, or -**app/Helpers** directory. CodeIgniter will look first in your -**app/Helpers** directory. If the directory does not exist or the -specified helper is not located there CI will instead look in your -global **system/Helpers** directory. +**app/Helpers** directory. -**************** -Loading a Helper -**************** +*************** +Loading Helpers +*************** .. note:: The URL helper is always loaded so you do not need to load it yourself. +Loading a Helper +================ + Loading a helper file is quite simple using the following method: .. literalinclude:: helpers/001.php @@ -58,6 +58,23 @@ For example, to load the **Cookie Helper** file, which is named .. note:: The Helper loading method above does not return a value, so don't try to assign it to a variable. Just use it as shown. +Auto-Discovery and Composer Packages +------------------------------------ + +By default, CodeIgniter will search for the helper files in all defined namespaces +by :ref:`auto-discovery`. +You can check your defined namespaces by the spark command. See :ref:`confirming-namespaces`. + +If you use many Composer packages, you will have many defined namespaces. +CodeIgniter will scan all namespaces by default. + +To avoid wasting time scanning for irrelevant Composer packages, you can manually +specify packages for Auto-Discovery. See :ref:`modules-specify-composer-packages` +for details. + +Or you can :ref:`specify a namespace ` +for a helper that you want to load. + Loading Multiple Helpers ======================== @@ -81,14 +98,14 @@ it. However if you want to load in your controller constructor, you can use the ``$helpers`` property in Controller instead. See :ref:`Controllers `. -.. _helpers-loading-from-non-standard-locations: +.. _helpers-loading-from-specified-namespace: -Loading from Non-standard Locations -=================================== +Loading from Specified Namespace +================================ Helpers can be loaded from directories outside of **app/Helpers** and -**system/Helpers**, as long as that path can be found through a namespace that -has been set up within the PSR-4 section of the :doc:`Autoloader config file <../concepts/autoloader>`. +**system/Helpers**, as long as that path can be found in defined namespaces. + You would prefix the name of the Helper with the namespace that it can be located in. Within that namespaced directory, the loader expects it to live within a sub-directory named **Helpers**. An example will help understand this. diff --git a/user_guide_src/source/general/modules.rst b/user_guide_src/source/general/modules.rst index c2173792ab4a..4e5c72a9fa9b 100644 --- a/user_guide_src/source/general/modules.rst +++ b/user_guide_src/source/general/modules.rst @@ -246,7 +246,7 @@ directory: .. literalinclude:: modules/009.php -You can specify namespaces. See :ref:`helpers-loading-from-non-standard-locations` for details. +You can specify namespaces. See :ref:`helpers-loading-from-specified-namespace` for details. Language Files ============== From 59ae2e62ed33028431ae14e37db1856a20950de3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 08:49:59 +0900 Subject: [PATCH 06/14] docs: fix typo in doc comment --- system/Common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Common.php b/system/Common.php index ebfd8e2565e8..80dca72c74e3 100644 --- a/system/Common.php +++ b/system/Common.php @@ -579,7 +579,7 @@ function function_usable(string $functionName): bool if (! function_exists('helper')) { /** * Loads a helper file into memory. Supports namespaced helpers, - * both in and out of the 'helpers' directory of a namespaced directory. + * both in and out of the 'Helpers' directory of a namespaced directory. * * Will load ALL helpers of the matching name, in the following order: * 1. app/Helpers From 26a3e75fe19bce3340d2f44cfdbfa7be61cdebf8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 18:58:12 +0900 Subject: [PATCH 07/14] test: add tests for exact_length --- tests/system/Validation/RulesTest.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/system/Validation/RulesTest.php b/tests/system/Validation/RulesTest.php index f031a129a286..23358d08904b 100644 --- a/tests/system/Validation/RulesTest.php +++ b/tests/system/Validation/RulesTest.php @@ -398,8 +398,10 @@ public function testMaxLengthReturnsFalseWithNonNumericVal(): void /** * @dataProvider provideExactLength + * + * @param int|string|null $data */ - public function testExactLength(?string $data, bool $expected): void + public function testExactLength($data, bool $expected): void { $this->validation->setRules(['foo' => 'exact_length[3]']); $this->assertSame($expected, $this->validation->run(['foo' => $data])); @@ -408,10 +410,13 @@ public function testExactLength(?string $data, bool $expected): void public static function provideExactLength(): iterable { yield from [ - 'null' => [null, false], - 'exact' => ['bar', true], - 'less' => ['ba', false], - 'greater' => ['bars', false], + 'null' => [null, false], + 'exact' => ['bar', true], + 'exact_int' => [123, true], + 'less' => ['ba', false], + 'less_int' => [12, false], + 'greater' => ['bars', false], + 'greater_int' => [1234, false], ]; } From 391723b1c8ffc9ac56cd0fd174a2e56f85413ce5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 24 Oct 2023 18:58:42 +0900 Subject: [PATCH 08/14] fix: exact_length does not validate int values --- system/Validation/StrictRules/Rules.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 7043646c1f58..0887eff28ada 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -63,6 +63,10 @@ public function equals($str, string $val): bool */ public function exact_length($str, string $val): bool { + if (is_int($str) || is_float($str)) { + $str = (string) $str; + } + if (! is_string($str)) { return false; } From 2969640d4a4bf2cf4d345e98faee6d42979e25b9 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 21 Oct 2023 15:54:00 +0800 Subject: [PATCH 09/14] Update phpstan-codeigniter to v1.4.1 --- composer.json | 2 +- phpstan.neon.dist | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 0151c1f982a8..9e0288c26f6b 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require-dev": { "codeigniter/coding-standard": "^1.5", - "codeigniter/phpstan-codeigniter": "^v1.1", + "codeigniter/phpstan-codeigniter": "^1.4", "ergebnis/composer-normalize": "^2.28", "fakerphp/faker": "^1.9", "kint-php/kint": "^5.0.4", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 1fcd28307cf5..4041710fb703 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -38,6 +38,3 @@ parameters: booleansInConditions: true disallowedConstructs: true matchingInheritedMethodNames: true - codeigniter: - additionalConfigNamespaces: - - CodeIgniter\Config\ From 94130de237e84aee778ca88604da6c0ac527356c Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 21 Oct 2023 15:59:29 +0800 Subject: [PATCH 10/14] Fix Modules does not extend BaseConfig --- phpstan-baseline.php | 10 ---------- system/Config/BaseConfig.php | 2 +- system/Config/BaseService.php | 8 ++------ .../Cache/FactoriesCacheFileVarExportHandlerTest.php | 2 -- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index d1c574463f58..0d372afdb97c 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -871,16 +871,6 @@ 'count' => 4, 'path' => __DIR__ . '/system/Config/BaseConfig.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Config/BaseConfig.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/system/Config/BaseService.php', -]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 3, diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 87166737e746..b4b9a4a21349 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -82,7 +82,7 @@ public static function __set_state(array $array) */ public function __construct() { - static::$moduleConfig = config(Modules::class); + static::$moduleConfig = new Modules(); if (! static::$override) { return; diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index b66b8c9f535e..22daf7b69e4b 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -330,9 +330,7 @@ public static function injectMock(string $name, $mock) protected static function discoverServices(string $name, array $arguments) { if (! static::$discovered) { - $config = config(Modules::class); - - if ($config->shouldDiscover('services')) { + if ((new Modules())->shouldDiscover('services')) { $locator = static::locator(); $files = $locator->search('Config/Services'); @@ -372,9 +370,7 @@ protected static function discoverServices(string $name, array $arguments) protected static function buildServicesCache(): void { if (! static::$discovered) { - $config = config(Modules::class); - - if ($config->shouldDiscover('services')) { + if ((new Modules())->shouldDiscover('services')) { $locator = static::locator(); $files = $locator->search('Config/Services'); diff --git a/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php index a3aff35b000e..8a2dba00703b 100644 --- a/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php +++ b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php @@ -15,7 +15,6 @@ use CodeIgniter\Config\Factories; use CodeIgniter\Test\CIUnitTestCase; use Config\App; -use Config\Modules; /** * @internal @@ -58,7 +57,6 @@ public function testSave() $this->assertArrayHasKey('aliases', $cachedData); $this->assertArrayHasKey('instances', $cachedData); - $this->assertArrayHasKey(Modules::class, $cachedData['aliases']); $this->assertArrayHasKey('App', $cachedData['aliases']); } From 89f1cc68ed44a7208d478ab3779b7118abf9a0c0 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 22 Oct 2023 19:02:06 +0800 Subject: [PATCH 11/14] Inject Modules into constructor --- system/Config/BaseConfig.php | 4 +-- tests/system/Config/BaseConfigTest.php | 36 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index b4b9a4a21349..0269194bdc4c 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -80,9 +80,9 @@ public static function __set_state(array $array) * * The "shortPrefix" is the lowercase-only config class name. */ - public function __construct() + public function __construct(?Modules $modules = null) { - static::$moduleConfig = new Modules(); + static::$moduleConfig = $modules ?? new Modules(); if (! static::$override) { return; diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index 2da390392ed6..ca615602a276 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -11,8 +11,11 @@ namespace CodeIgniter\Config; +use CodeIgniter\Autoloader\FileLocator; use CodeIgniter\Test\CIUnitTestCase; +use Config\Modules; use Encryption; +use PHPUnit\Framework\MockObject\MockObject; use RegistrarConfig; use RuntimeException; use SimpleConfig; @@ -45,6 +48,8 @@ protected function setUp(): void if (! class_exists('Encryption', false)) { require $this->fixturesFolder . '/Encryption.php'; } + + BaseConfig::$registrars = []; } public function testBasicValues(): void @@ -265,32 +270,27 @@ public function testBadRegistrar(): void $this->assertSame('bar', $config->foo); } - public function testNotEnabled(): void + public function testDiscoveryNotEnabledWillNotPopulateRegistrarsArray(): void { - $modulesConfig = config('Modules'); - $modulesConfig->enabled = false; - - $config = new RegistrarConfig(); - $config::$registrars = []; - $expected = $config::$registrars; + /** @var MockObject&Modules $modules */ + $modules = $this->createMock(Modules::class); + $modules->method('shouldDiscover')->with('registrars')->willReturn(false); - $method = $this->getPrivateMethodInvoker($config, 'registerProperties'); - $method(); + $config = new RegistrarConfig($modules); - $this->assertSame($expected, $config::$registrars); + $this->assertSame([], $config::$registrars); } - public function testDidDiscovery(): void + public function testRedoingDiscoveryWillStillSetDidDiscoveryPropertyToTrue(): void { - $modulesConfig = config('Modules'); - $modulesConfig->enabled = true; + /** @var FileLocator&MockObject $locator */ + $locator = $this->createMock(FileLocator::class); + $locator->method('search')->with('Config/Registrar.php')->willReturn([]); + Services::injectMock('locator', $locator); - $config = new RegistrarConfig(); - $config::$registrars = []; + $config = new RegistrarConfig(); $this->setPrivateProperty($config, 'didDiscovery', false); - - $method = $this->getPrivateMethodInvoker($config, 'registerProperties'); - $method(); + ($this->getPrivateMethodInvoker($config, 'registerProperties'))(); $this->assertTrue($this->getPrivateProperty($config, 'didDiscovery')); } From 314210848d990d26420fab5d53bba88c626a4d24 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Tue, 24 Oct 2023 11:34:32 +0800 Subject: [PATCH 12/14] Use static setter --- system/Config/BaseConfig.php | 15 ++++++++++++--- tests/system/Config/BaseConfigTest.php | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 0269194bdc4c..7daa6e1544c0 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -55,7 +55,7 @@ class BaseConfig /** * The modules configuration. * - * @var Modules + * @var Modules|null */ protected static $moduleConfig; @@ -74,15 +74,24 @@ public static function __set_state(array $array) return $obj; } + /** + * @internal For testing purposes only. + * @testTag + */ + public static function setModules(Modules $modules): void + { + static::$moduleConfig = $modules; + } + /** * Will attempt to get environment variables with names * that match the properties of the child class. * * The "shortPrefix" is the lowercase-only config class name. */ - public function __construct(?Modules $modules = null) + public function __construct() { - static::$moduleConfig = $modules ?? new Modules(); + static::$moduleConfig ??= new Modules(); if (! static::$override) { return; diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index ca615602a276..76861ccb12ad 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -50,6 +50,7 @@ protected function setUp(): void } BaseConfig::$registrars = []; + BaseConfig::setModules(new Modules()); // reset to clean copy of Modules } public function testBasicValues(): void @@ -276,7 +277,8 @@ public function testDiscoveryNotEnabledWillNotPopulateRegistrarsArray(): void $modules = $this->createMock(Modules::class); $modules->method('shouldDiscover')->with('registrars')->willReturn(false); - $config = new RegistrarConfig($modules); + RegistrarConfig::setModules($modules); + $config = new RegistrarConfig(); $this->assertSame([], $config::$registrars); } From 61bbbca81163dfc61af410972395de9cd905e637 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 25 Oct 2023 09:02:56 +0800 Subject: [PATCH 13/14] Simplify test --- tests/system/Config/BaseConfigTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Config/BaseConfigTest.php b/tests/system/Config/BaseConfigTest.php index 76861ccb12ad..958387cc6b6d 100644 --- a/tests/system/Config/BaseConfigTest.php +++ b/tests/system/Config/BaseConfigTest.php @@ -290,9 +290,9 @@ public function testRedoingDiscoveryWillStillSetDidDiscoveryPropertyToTrue(): vo $locator->method('search')->with('Config/Registrar.php')->willReturn([]); Services::injectMock('locator', $locator); + $this->setPrivateProperty(RegistrarConfig::class, 'didDiscovery', false); + $config = new RegistrarConfig(); - $this->setPrivateProperty($config, 'didDiscovery', false); - ($this->getPrivateMethodInvoker($config, 'registerProperties'))(); $this->assertTrue($this->getPrivateProperty($config, 'didDiscovery')); } From c64959eb84b6b44e154248d3aceb4c21664a2643 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 26 Oct 2023 05:45:40 +0900 Subject: [PATCH 14/14] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/models/model.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index 880adfc0d9cb..db4ecd80b59d 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -69,7 +69,7 @@ Connecting to the Database ========================== When the class is first instantiated, if no database connection instance is passed to the constructor, -and if you don't set the ``$DBGroup`` property to your model class, +and if you don't set the ``$DBGroup`` property on your model class, it will automatically connect to the default database group, as set in the database configuration. You can