Search code examples
phpsymfonycomposer-php

How to force Composer script to load local class instead of global class


undefined method

(Relevant files linked at the bottom of my question.)

I let Composer run some post-install-cmd and post-update-cmd scripts. In my script I want to make use of the readlink() function from symfony/filesystem. Inside my projects /vendor folder there is the 3.4 version of the filesystem package, fine.

I use Symfony\Component\Filesystem\Filesystem; at the top of my file.

But whenever I run:

$fs = new Filesystem();
$path = '/path/to/some/symlink';
if ($fs->readlink($path)) {
  // code
}

I get the following error which tells me I'm calling an undefined method:

PHP Fatal error: Uncaught Error: Call to undefined method Symfony\Component\Filesystem\Filesystem::readlink() in /Users/leymannx/Sites/blog/scripts/composer/ScriptHandler.php:160

OK, so I double-checked the class inside my project's /vendor folder. This method is there. My IDE points me there. But when I run:

$fs = new Filesystem();
get_class_methods($fs);

this method is not listed.

Which file is it trying to load the method from?

OK, so I tried to check which file it's loading this class from:

$fs = new Filesystem();
$a = new \ReflectionClass($fs);
echo $a->getFileName();

and that returns me phar:///usr/local/Cellar/composer/1.7.2/bin/composer/vendor/symfony/filesystem/Filesystem.php – But why? Why is it taking the package from my Mac's Cellar? That's odd.

But OK, so I thought that's a Homebrew issue, and uninstalled the Homebrew Composer $ brew uninstall --force composer and installed it again as PHAR like documented on https://getcomposer.org/doc/00-intro.md#globally.

But now it's the same.

$fs = new Filesystem();
$a = new \ReflectionClass($fs);
echo $a->getFileName();

returns me phar:///usr/local/bin/composer/vendor/symfony/filesystem/Filesystem.php.

But why? Why does it pick up the (outdated) package from my global Composer installation? How can I force my script to use the project's local class and not the one from my global Composer installation?

What else?

Initially my $PATH contained /Users/leymannx/.composer/vendor/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin. I removed /Users/leymannx/.composer/vendor/bin to only return /usr/local/bin /usr/bin /bin /usr/sbin /sbin. Still the same.

I also tried setting the following in my composer.json. Still the same:

"optimize-autoloader": true,
"classmap-authoritative": true,
"vendor-dir": "vendor/",

I finally created an issue on GitHub: https://github.com/composer/composer/issues/7708



Solution

  • Finally fixed it by requiring require_once __DIR__.'/../vendor/autoload.php' at the top of my script and leaving everything else untouched.

    On the Filesystem Component docs page it's also written:

    If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer.