diff --git a/admin/starter/composer.json b/admin/starter/composer.json index 37c3e5642f29..35b791c3f050 100644 --- a/admin/starter/composer.json +++ b/admin/starter/composer.json @@ -19,6 +19,9 @@ "phpunit/phpunit": "^9.1" }, "autoload": { + "psr-4": { + "App\\": "app/" + }, "exclude-from-classmap": [ "**/Database/Migrations/**" ] diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 22f05ecdab26..85e0d9919d2b 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -30,23 +30,18 @@ class Autoload extends AutoloadConfig * their location on the file system. These are used by the autoloader * to locate files the first time they have been instantiated. * - * The '/app' and '/system' directories are already mapped for you. - * you may change the name of the 'App' namespace if you wish, + * The 'Config' (APPPATH . 'Config') and 'CodeIgniter' (SYSTEMPATH) are + * already mapped for you. + * + * You may change the name of the 'App' namespace if you wish, * but this should be done prior to creating any namespaced classes, * else you will need to modify all of those classes for this to work. * - * Prototype: - * $psr4 = [ - * 'CodeIgniter' => SYSTEMPATH, - * 'App' => APPPATH - * ]; - * * @var array|string> * @phpstan-var array> */ public $psr4 = [ - APP_NAMESPACE => APPPATH, // For custom app namespace - 'Config' => APPPATH . 'Config', + APP_NAMESPACE => APPPATH, ]; /** diff --git a/composer.json b/composer.json index 2466dd9742d6..c20c5a2e5b41 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,8 @@ }, "autoload": { "psr-4": { - "CodeIgniter\\": "system/" + "CodeIgniter\\": "system/", + "Config\\": "app/Config/" }, "exclude-from-classmap": [ "**/Database/Migrations/**" diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index a744fb13d08a..e54f8d732d84 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -141,8 +141,6 @@ private function loadComposerAutoloader(Modules $modules): void /** @var ClassLoader $composer */ $composer = include COMPOSER_PATH; - $this->loadComposerClassmap($composer); - // Should we load through Composer's namespaces, also? if ($modules->discoverInComposer) { // @phpstan-ignore-next-line @@ -159,11 +157,11 @@ private function loadComposerAutoloader(Modules $modules): void */ public function register() { - // Prepend the PSR4 autoloader for maximum performance. - spl_autoload_register([$this, 'loadClass'], true, true); + // Register classmap loader for the files in our class map. + spl_autoload_register([$this, 'loadClassmap'], true); - // Now prepend another loader for the files in our class map. - spl_autoload_register([$this, 'loadClassmap'], true, true); + // Register the PSR-4 autoloader. + spl_autoload_register([$this, 'loadClass'], true); // Load our non-class files foreach ($this->files as $file) { @@ -366,9 +364,13 @@ private function loadComposerNamespaces(ClassLoader $composer, array $composerPa { $namespacePaths = $composer->getPrefixesPsr4(); - // Get rid of CodeIgniter so we don't have duplicates - if (isset($namespacePaths['CodeIgniter\\'])) { - unset($namespacePaths['CodeIgniter\\']); + // Get rid of duplicated namespaces. + $duplicatedNamespaces = ['CodeIgniter', APP_NAMESPACE, 'Config']; + + foreach ($duplicatedNamespaces as $ns) { + if (isset($namespacePaths[$ns . '\\'])) { + unset($namespacePaths[$ns . '\\']); + } } if (! method_exists(InstalledVersions::class, 'getAllRawData')) { @@ -432,13 +434,6 @@ private function loadComposerNamespaces(ClassLoader $composer, array $composerPa $this->addNamespace($newPaths); } - private function loadComposerClassmap(ClassLoader $composer): void - { - $classes = $composer->getClassMap(); - - $this->classmap = array_merge($this->classmap, $classes); - } - /** * Locates autoload information from Composer, if available. * diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index da08dd6f6640..3f0dc494f0a9 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -92,7 +92,7 @@ class AutoloadConfig */ protected $corePsr4 = [ 'CodeIgniter' => SYSTEMPATH, - 'App' => APPPATH, // To ensure filters, etc still found, + 'Config' => APPPATH . 'Config', ]; /** diff --git a/tests/system/Commands/Utilities/NamespacesTest.php b/tests/system/Commands/Utilities/NamespacesTest.php index 755dd70f7cc6..d700dd666ec6 100644 --- a/tests/system/Commands/Utilities/NamespacesTest.php +++ b/tests/system/Commands/Utilities/NamespacesTest.php @@ -56,8 +56,8 @@ public function testNamespacesCommandCodeIgniterOnly(): void | Namespace | Path | Found? | +---------------+-------------------------+--------+ | CodeIgniter | ROOTPATH/system | Yes | - | App | ROOTPATH/app | Yes | | Config | APPPATH/Config | Yes | + | App | ROOTPATH/app | Yes | | Tests\Support | ROOTPATH/tests/_support | Yes | +---------------+-------------------------+--------+ EOL; diff --git a/tests/system/Config/ConfigTest.php b/tests/system/Config/ConfigTest.php index 6bd4103a7aa2..b00bc2b9f699 100644 --- a/tests/system/Config/ConfigTest.php +++ b/tests/system/Config/ConfigTest.php @@ -46,13 +46,6 @@ public function testCreateSharedInstance(): void $this->assertSame($Config2, $Config); } - public function testCreateNonConfig(): void - { - $Config = Config::get('Constants', false); - - $this->assertNull($Config); - } - /** * @runInSeparateProcess * @preserveGlobalState disabled diff --git a/user_guide_src/source/changelogs/v4.5.0.rst b/user_guide_src/source/changelogs/v4.5.0.rst index 3f509d73431f..f109180c8009 100644 --- a/user_guide_src/source/changelogs/v4.5.0.rst +++ b/user_guide_src/source/changelogs/v4.5.0.rst @@ -116,6 +116,10 @@ Helpers and Functions Others ====== +- **Autoloader:** Autoloading performance when using Composer has been improved. + Adding the ``App`` namespace in the ``autoload.psr4`` setting in **composer.json** + may also improve the performance of your app. See :ref:`autoloader-application-namespace`. + Message Changes *************** diff --git a/user_guide_src/source/concepts/autoloader.rst b/user_guide_src/source/concepts/autoloader.rst index 22722716661b..cc47162e5c0c 100644 --- a/user_guide_src/source/concepts/autoloader.rst +++ b/user_guide_src/source/concepts/autoloader.rst @@ -13,6 +13,7 @@ classes that your project is using. Keeping track of where every single file is, hard-coding that location into your files in a series of ``requires()`` is a massive headache and very error-prone. That's where autoloaders come in. +*********************** CodeIgniter4 Autoloader *********************** @@ -36,12 +37,14 @@ beginning of the framework's execution. file name case is incorrect, the autoloader cannot find the file on the server. +************* 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. +********** Namespaces ********** @@ -61,21 +64,53 @@ The value is the location to the directory the classes can be found in. php spark namespaces +.. _autoloader-application-namespace: + +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. +Config Namespace +---------------- + +Config files are namespaced in the ``Config`` namespace, not in ``App\Config`` as you might +expect. This allows the core system files to always be able to locate them, even when the application +namespace has changed. + +Changing App Namespace +---------------------- + You may change this namespace by editing the **app/Config/Constants.php** file and setting the new namespace value under the ``APP_NAMESPACE`` setting: .. literalinclude:: autoloader/002.php :lines: 2- -You will need to modify any existing files that are referencing the current namespace. +And if you use Composer autoloader, you also need to change the ``App`` namespace +in your **composer.json**, and run ``composer dump-autoload``. -.. important:: Config files are namespaced in the ``Config`` namespace, not in ``App\Config`` as you might - expect. This allows the core system files to always be able to locate them, even when the application - namespace has changed. +.. code-block:: text + { + ... + "autoload": { + "psr-4": { + "App\\": "app/" <-- Change + }, + ... + }, + ... + } + +.. note:: Since v4.5.0 appstarter, the ``App\\`` namespace has been added to + **composer.json**'s ``autoload.psr-4``. If your **composer.json** does not + have it, adding it may improve your app's autoloading performance. + +You will need to modify any existing files that are referencing the current namespace. + +******** Classmap ******** @@ -87,12 +122,21 @@ third-party libraries that are not namespaced: The key of each row is the name of the class that you want to locate. The value is the path to locate it at. +**************** Composer Support **************** -Composer support is automatically initialized by default. By default, it looks for Composer's autoload file at +Composer support is automatically initialized by default. + +By default, it looks for Composer's autoload file at ``ROOTPATH . 'vendor/autoload.php'``. If you need to change the location of that file for any reason, you can modify the value defined in **app/Config/Constants.php**. -.. note:: If the same namespace is defined in both CodeIgniter and Composer, CodeIgniter's autoloader will be +Priority of Autoloaders +======================= + +If the same namespace is defined in both CodeIgniter and Composer, Composer's +autoloader will be the first one to get a chance to locate the file. + +.. note:: Prior to v4.5.0, if the same namespace was defined in both CodeIgniter and Composer, CodeIgniter's autoloader was the first one to get a chance to locate the file. diff --git a/user_guide_src/source/concepts/autoloader/001.php b/user_guide_src/source/concepts/autoloader/001.php index 68eeaddd625f..d6da1bce5389 100644 --- a/user_guide_src/source/concepts/autoloader/001.php +++ b/user_guide_src/source/concepts/autoloader/001.php @@ -8,8 +8,7 @@ class Autoload extends AutoloadConfig { // ... public $psr4 = [ - APP_NAMESPACE => APPPATH, // For custom app namespace - 'Config' => APPPATH . 'Config', + APP_NAMESPACE => APPPATH, ]; // ... diff --git a/user_guide_src/source/dbmgmt/migration.rst b/user_guide_src/source/dbmgmt/migration.rst index 4af207164b73..3f7a17e71024 100644 --- a/user_guide_src/source/dbmgmt/migration.rst +++ b/user_guide_src/source/dbmgmt/migration.rst @@ -93,6 +93,7 @@ For example, assume that we have the following namespaces defined in our Autoloa configuration file: .. literalinclude:: migration/004.php + :lines: 2- This will look for any migrations located at both **APPPATH/Database/Migrations** and **ROOTPATH/MyCompany/Database/Migrations**. This makes it simple to include migrations in your diff --git a/user_guide_src/source/dbmgmt/migration/004.php b/user_guide_src/source/dbmgmt/migration/004.php index 1e4c831c8219..24c684088a4b 100644 --- a/user_guide_src/source/dbmgmt/migration/004.php +++ b/user_guide_src/source/dbmgmt/migration/004.php @@ -1,6 +1,6 @@ APPPATH, - 'MyCompany' => ROOTPATH . 'MyCompany', + APP_NAMESPACE => APPPATH, + 'MyCompany' => ROOTPATH . 'MyCompany', ]; diff --git a/user_guide_src/source/general/modules/001.php b/user_guide_src/source/general/modules/001.php index 1f93e213359f..dcc1fbe3a9e4 100644 --- a/user_guide_src/source/general/modules/001.php +++ b/user_guide_src/source/general/modules/001.php @@ -6,9 +6,9 @@ class Autoload extends AutoloadConfig { + // ... public $psr4 = [ - APP_NAMESPACE => APPPATH, // For custom namespace - 'Config' => APPPATH . 'Config', + APP_NAMESPACE => APPPATH, 'Acme\Blog' => ROOTPATH . 'acme/Blog', ];