2

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


0

2 Answers 2

1

This is matter of context where your code is run. If you're executing some method directly in post-install-cmd it will be executed inside of Composer's process. It means that it will share all code bundled inside of composer.phar. Since you can't have two classes with the same FQN, you can't include another Symfony\Component\Filesystem\Filesystem in this context.

You can bypass this by running your script inside of separate process. You may create post-install-cmd.php file where you do all bootstrapping (like require vendor/autoload.php) and call these methods. Then run this file in your post-install-cmd hook:

"scripts": {
    "post-install-cmd": [
        "php post-install-cmd.php"
    ]
},
Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.