dunglas / frankenphp

🧟 The modern PHP app server
https://frankenphp.dev
MIT License
6.78k stars 229 forks source link

Error Installing Laravel with composer using franken's php-cli #483

Closed ARehmanMahi closed 7 months ago

ARehmanMahi commented 8 months ago

Following the discussion here https://github.com/dunglas/frankenphp/issues/470#issuecomment-1898094157

I'm trying to use frankenphp php-cli to run composer commands.

I just ran ./frankenphp php-cli ./composer.phar create-project laravel/laravel example-app In an Empty Folder. And get the following error. This is a composer command to install laravel project.

Edit 1

Very strange behavior just unfolded. Note: I've system-level composer & php8.3 already installed

I renamed php folder /etc/php/8.3 to 8.3.off to be sure it is using Franky's build-in php Hence the error after the above command. BUT If I keep the system php as it is (not renamed off), then the installer worked fine. To me this feels like the composer or franken php-cli used system's php somehow, maybe?

Edit 2

I ran php version and path in a file and here's the output. $ ./frankenphp php-cli info.php PHP Version: 8.3.1 PHP Path: /var/www/html/test/./frankenphp

$ php info.php PHP Version: 8.3.1 PHP Path: /usr/bin/php

Edit 3

/frankenphp php-cli uses Web SAPI php uses CLI That difference is the cause maybe?

> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi

Fatal error: Uncaught ReflectionException: Class "config" does not exist in /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php:912
Stack trace:
#0 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(912): ReflectionClass->__construct('config')
#1 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(795): Illuminate\Container\Container->build('config')
#2 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(957): Illuminate\Container\Container->resolve('config', Array, true)
#3 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(731): Illuminate\Foundation\Application->resolve('config', Array)
#4 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(942): Illuminate\Container\Container->make('config', Array)
#5 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(1454): Illuminate\Foundation\Application->make('config')
#6 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(560): Illuminate\Container\Container->offsetGet('config')
#7 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(611): Illuminate\Log\LogManager->getDefaultDriver()
#8 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(124): Illuminate\Log\LogManager->parseDriver(NULL)
#9 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(681): Illuminate\Log\LogManager->driver()
#10 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(317): Illuminate\Log\LogManager->error('Call to undefin...', Array)
#11 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(278): Illuminate\Foundation\Exceptions\Handler->reportThrowable(Object(Error))
#12 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(523): Illuminate\Foundation\Exceptions\Handler->report(Object(Error))
#13 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(203): Illuminate\Foundation\Console\Kernel->reportException(Object(Error))
#14 /var/www/html/test/example-app/artisan(35): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#15 {main}

Next Illuminate\Contracts\Container\BindingResolutionException: Target class [config] does not exist. in /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php:914
Stack trace:
#0 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(795): Illuminate\Container\Container->build('config')
#1 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(957): Illuminate\Container\Container->resolve('config', Array, true)
#2 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(731): Illuminate\Foundation\Application->resolve('config', Array)
#3 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(942): Illuminate\Container\Container->make('config', Array)
#4 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php(1454): Illuminate\Foundation\Application->make('config')
#5 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(560): Illuminate\Container\Container->offsetGet('config')
#6 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(611): Illuminate\Log\LogManager->getDefaultDriver()
#7 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(124): Illuminate\Log\LogManager->parseDriver(NULL)
#8 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(681): Illuminate\Log\LogManager->driver()
#9 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(317): Illuminate\Log\LogManager->error('Call to undefin...', Array)
#10 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(278): Illuminate\Foundation\Exceptions\Handler->reportThrowable(Object(Error))
#11 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(523): Illuminate\Foundation\Exceptions\Handler->report(Object(Error))
#12 /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(203): Illuminate\Foundation\Console\Kernel->reportException(Object(Error))
#13 /var/www/html/test/example-app/artisan(35): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#14 {main}
  thrown in /var/www/html/test/example-app/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 914
Script @php artisan package:discover --ansi handling the post-autoload-dump event returned with error code 255

composer.phar

Downloaded from the site directly.

frankenphp v1.0.3

Downloaded from the git directly.

How to reproduce on Ubuntu 22.04

$ mkdir test
$ cd test
# copy composer.phar & frankenphp here
$ ls > composer.phar frankenphp
$ ./frankenphp php-cli ./composer.phar create-project laravel/laravel example-app

Laravel installer runs fine untill it reaches pro-install script and error outs This also happens on an existing project.

Using system-level php with composer works fine so it seems to be frankenphp php-cli's issue maybe.

withinboredom commented 8 months ago

I suspect (without digging into laravel's template repo), that some script is using:

{
  "scripts": {"example": "composer do-stuff"}
}

instead of

{
  "scripts": {"example": "@composer do-stuff"}
}

The former would use a globally installed composer (if available) while the latter should call back into itself. It'd be interesting to test both these variations in a project and see what happens.

ARehmanMahi commented 8 months ago

I didn't now what the @ sign did in composer file, just got the info from your comment, Thank you.

Every command in laravel's composer has @php, but I think the error is in postAutoloadDump.

composer.json

"scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    },

This has gone out of my current knowledge of composer & the internal framework. This is an interesting outcome, and most probably at Laravel's end itself.

I'm not sure if I can help any further, but let me know. And maybe close this issue as it seems out of Franky's scope.

withinboredom commented 8 months ago

Ah, interesting. It's possible that FrankenPHP doesn't let composer know how to call @php.

If we take a look at composer it is actually trying to execute a php binary, which would default to frankenphp in this case.

It then tries to call frankenphp with -d arguments, which will fail.

So, it looks like if we want full php-cli support, we'd need to have a php binary that can be used by composer (can use environment variable: PHP_BINARY).

withinboredom commented 8 months ago

@dunglas ^

We might need to possibly extract the static php binary to a location on disk and set PHP_BINARY env var when executing cli scripts (particularly composer).

or provide a fully backwards compatible cli. or ignore unsupported arguments.

ARehmanMahi commented 8 months ago

That's a great find, you are awesome.

I know dunglas mentioned php parameters are not supported yet, so this would require some extra work. Glad my discussion helped in pointing out some not-so-common use cases.

Simple approaches are not so simple after all :)

dunglas commented 8 months ago

Thanks for the investigation!

In my opinion we should:

WDYT?

withinboredom commented 8 months ago

Composer uses the PHP_BINARY env variable if it's set, we just need to test that it handles spaces for our case. Meaning will it call "frankenphp php-cli" script.php or "frankenphp php-cli script.php".

flexponsive commented 7 months ago

I ran into the same problem - in a laravel project, composer wants to a call php binary e.g. to do package discovery. As a workaround, I created a shell script in /usr/local/bin/php to strip the -d parameters and call frankenphp:

#!/bin/bash

# iterate over $@. if the current argument is -d, remove the argument and the next one
for i in "$@"
do
    if [ "$i" == "-d" ]; then
        shift
        shift
    fi
done

/usr/local/bin/frankenphp php-cli "$@"

With this, the composer install goes through.

dunglas commented 7 months ago

Nice one. Would you mind opening a docs PR to mention this trick? Maybe in the known-issues.md file?

dunglas commented 7 months ago

Closing for now: https://github.com/dunglas/frankenphp/pull/610