Skip to content

Unset Composer's autoloader from the autoload stack #5834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions system/Autoloader/Autoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ protected function discoverComposerNamespaces()
$paths = $composer->getPrefixesPsr4();
$classes = $composer->getClassMap();

$composer->unregister();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in the discoverComposerNamespaces() method.
So when $modules->discoverInComposer is false, it does not run.
When $modules->discoverInComposer is false, Composer autoloader is still enabled.
Is it intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Composer's autoloader is only enabled when included in $this->discoverComposerNamespaces(). In the code, once we include COMPOSER_PATH, Composer will register its autoloader and prepend it in the autoload stack. If $modules->discoverInComposer is false, then COMPOSER_PATH is not included and won't be registered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<?php

namespace App\Controllers;

use Closure;

class Home extends BaseController
{
    public function index()
    {
        foreach (spl_autoload_functions() as $autoloader) {
            switch ($autoloader) {
                case $autoloader instanceof Closure:
                    echo get_class($autoloader) . PHP_EOL;
                    break;

                case is_string($autoloader[0]):
                    echo $autoloader[0] . '::' . $autoloader[1] . PHP_EOL;
                    break;

                case is_object($autoloader[0]):
                    echo get_class($autoloader[0]) . '::' . $autoloader[1] . PHP_EOL;
                    break;
            }
        }
    }
}

With this PR, there is no Composer autoloader.

$ php public/index.php 
CodeIgniter\Autoloader\Autoloader::loadClassmap
CodeIgniter\Autoloader\Autoloader::loadClass
PHPStan\PharAutoloader::loadClass
Closure

Next, make $discoverInComposer false.

--- a/app/Config/Modules.php
+++ b/app/Config/Modules.php
@@ -29,7 +29,7 @@ class Modules extends BaseModules
      *
      * @var bool
      */
-    public $discoverInComposer = true;
+    public $discoverInComposer = false;
 
     /**
      * --------------------------------------------------------------------------
$ php public/index.php 
Composer\Autoload\ClassLoader::loadClass
CodeIgniter\Autoloader\Autoloader::loadClassmap
CodeIgniter\Autoloader\Autoloader::loadClass
PHPStan\PharAutoloader::loadClass
Closure

Composer autoloader is registered.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. So there's a place in the application lifecycle where Composer is included regardless of the $discoverInComposer flag. I'll take a look into it but most probably it shouldn't be included automatically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found it!

In system/bootstrap.php and system/Test/bootstrap.php, we require_once COMPOSER_PATH regardless of the $discoverInComposer flag, which shouldn't be the case as it is handled already in the Services::autoloader()->initialize() call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (is_file(COMPOSER_PATH)) {
    require_once COMPOSER_PATH;
}

unset($composer);

// Get rid of CodeIgniter so we don't have duplicates
Expand Down
2 changes: 1 addition & 1 deletion system/CLI/Commands.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function discoverCommands()
foreach ($files as $file) {
$className = $locator->findQualifiedNameFromPath($file);

if (empty($className) || ! class_exists($className)) {
if ($className === false) {
continue;
}

Expand Down
5 changes: 0 additions & 5 deletions system/Test/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@ class_alias('Config\Services', 'CodeIgniter\Services');
// Initialize and register the loader with the SPL autoloader stack.
Services::autoloader()->initialize(new Autoload(), new Modules())->register();

// Now load Composer's if it's available
if (is_file(COMPOSER_PATH)) {
require_once COMPOSER_PATH;
}

// Load environment settings from .env files into $_SERVER and $_ENV
require_once SYSTEMPATH . 'Config/DotEnv.php';

Expand Down
19 changes: 7 additions & 12 deletions system/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,13 @@ class_alias('Config\Services', 'CodeIgniter\Services');
// Initialize and register the loader with the SPL autoloader stack.
Services::autoloader()->initialize(new Autoload(), new Modules())->register();

// Now load Composer's if it's available
if (is_file(COMPOSER_PATH)) {
/*
* The path to the vendor directory.
*
* We do not want to enforce this, so set the constant if Composer was used.
*/
if (! defined('VENDORPATH')) {
define('VENDORPATH', dirname(COMPOSER_PATH) . DIRECTORY_SEPARATOR);
}

require_once COMPOSER_PATH;
/*
* The path to the vendor directory.
*
* We do not want to enforce this, so set the constant if Composer was used.
*/
if (is_file(COMPOSER_PATH) && ! defined('VENDORPATH')) {
define('VENDORPATH', dirname(COMPOSER_PATH) . DIRECTORY_SEPARATOR);
}

// Load environment settings from .env files into $_SERVER and $_ENV
Expand Down