xdebug-handler icon indicating copy to clipboard operation
xdebug-handler copied to clipboard

PHP Warning: proc_open(): Exec failed: No such file or directory in phar

Open akaDJon opened this issue 1 year ago • 4 comments
trafficstars

php 8.1.23 + Xdebug 3 Debian GNU/Linux 11 (bullseye) nginx 1.18 + php-fpm Laravel 10.33.0 Composer version 2.7.4

I get this error

PHP Warning:  proc_open(): Exec failed: No such file or directory in phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php on line 300 
PHP Stack trace:
PHP   1. {main}() /usr/local/bin/composer_php81:0
PHP   2. require() /usr/local/bin/composer_php81:29
PHP   3. Composer\XdebugHandler\XdebugHandler->check() phar:///usr/local/bin/composer_php81/bin/composer:29
PHP   4. Composer\XdebugHandler\XdebugHandler->restart($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/NylWn5', 4 => '/usr/local/bin/composer', 5 => 'install', 6 => '--dry-run']) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:149 
PHP   5. Composer\XdebugHandler\XdebugHandler->doRestart($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/NylWn5', 4 => '/usr/local/bin/composer', 5 => 'install', 6 => '--dry-run']) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:274 
PHP   6. proc_open($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/NylWn5', 4 => '/usr/local/bin/composer', 5 => 'install', 6 => '--dry-run'], $descriptor_spec = [], $pipes = NULL) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:300 
Return code: 127

I try run this script from browser. (over nginx+php-fpm)

        $descriptors = [
            0 => ['pipe', 'r'], // stdin
            1 => ['pipe', 'w'], // stdout
            2 => ['pipe', 'w']  // stderr
        ];

        $process = proc_open('composer install --dry-run', $descriptors, $pipes);

        if (is_resource($process)) {
            fclose($pipes[0]);

            $output = stream_get_contents($pipes[1]);
            fclose($pipes[1]);

            $errors = stream_get_contents($pipes[2]);
            fclose($pipes[2]);

            $returnCode = proc_close($process);

            dump("Output: $output");
            dump("Errors: $errors");
            dump("Return code: $returnCode");
        }

akaDJon avatar May 05 '24 06:05 akaDJon

Fixed the problem by explicitly specifying the paths to php and composer

$process = proc_open('/usr/bin/php8.1 /usr/local/bin/composer install --dry-run', $descriptors, $pipes);

akaDJon avatar May 05 '24 07:05 akaDJon

Thanks. We obviously need to check that PHP_BINARY isn't an empty string.

I'm curious to see what happens if you run:

$process = proc_open(['composer', 'install', '--dry-run'], $descriptors, $pipes);

johnstevenson avatar May 05 '24 11:05 johnstevenson

Thanks. We obviously need to check that PHP_BINARY isn't an empty string.

I'm curious to see what happens if you run:

$process = proc_open(['composer', 'install', '--dry-run'], $descriptors, $pipes);

the same error

Errors: PHP Warning:  proc_open(): Exec failed: No such file or directory in phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php on line 300 ◀
PHP Stack trace:
PHP   1. {main}() /usr/local/bin/composer_php81:0
PHP   2. require() /usr/local/bin/composer_php81:29
PHP   3. Composer\XdebugHandler\XdebugHandler->check() phar:///usr/local/bin/composer_php81/bin/composer:29
PHP   4. Composer\XdebugHandler\XdebugHandler->restart($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/Gflhtd', 4 => '/usr/bin/composer', 5 => 'install', 6 => '--dry-run']) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:149 ◀
PHP   5. Composer\XdebugHandler\XdebugHandler->doRestart($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/Gflhtd', 4 => '/usr/bin/composer', 5 => 'install', 6 => '--dry-run']) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:274 ◀
PHP   6. proc_open($command = [0 => '', 1 => '-n', 2 => '-c', 3 => '/tmp/Gflhtd', 4 => '/usr/bin/composer', 5 => 'install', 6 => '--dry-run'], $descriptor_spec = [], $pipes = NULL) phar:///usr/local/bin/composer_php81/vendor/composer/xdebug-handler/src/XdebugHandler.php:300

akaDJon avatar May 05 '24 14:05 akaDJon

the same error if

$process = proc_open(['php', '/usr/bin/composer', 'install', '--dry-run'], $descriptors, $pipes);

akaDJon avatar May 05 '24 14:05 akaDJon

Many thanks for the extra information. This is now fixed in 925e444

johnstevenson avatar May 06 '24 17:05 johnstevenson

I will check on next composer release)

akaDJon avatar May 07 '24 06:05 akaDJon

You can try now with composer self-update --snapshot, I updated to the latest xdebug-handler.

Seldaek avatar May 08 '24 09:05 Seldaek

You can try now with composer self-update --snapshot, I updated to the latest xdebug-handler.

I see. Xdebug still working and Composer dont do restart. It's fixed trouble, but maybe try fix search PHP_BINARY?

akaDJon avatar May 10 '24 04:05 akaDJon

Which SAPI are you using? Because it seems from the php sources that this may be the reason it's empty if the SAPI does not support that feature https://github.com/php/php-src/blob/8276560e65830abe4bfc9e964bebcb31f75eff5f/main/main.c#L368

Seldaek avatar May 10 '24 06:05 Seldaek

dump(PHP_BINARY);

/usr/sbin/php-fpm8.1

it is not standart php file: /usr/bin/php8.1 but all exists

root@dev# ls /usr/sbin/php-fpm8.1
-rwxr-xr-x 1 root root 5504136 Oct  6  2023 /usr/sbin/php-fpm8.1
root@dev# ls /usr/bin/php8.1
-rwxr-xr-x 1 root root 5530984 Oct  6  2023 /usr/bin/php8.1

akaDJon avatar May 10 '24 07:05 akaDJon

maybe composer get wrong PHP_BINARY after started from FPM

akaDJon avatar May 10 '24 07:05 akaDJon

maybe composer get wrong PHP_BINARY after started from FPM

Just to be clear, you are creating a new PHP CLI process from fpm in order to run composer.phar. If you give proc_open the full path to the php executable, then PHP_BINARY is present, but if you call php (or composer) then PHP_BINARY is empty.

PHP_BINARY is set from the sapi executable_location, which is argv[0] (ie. the first argument given to the program): https://github.com/php/php-src/blob/f88fc9c6e8262fe48eb9cf078e32e63af19047f2/sapi/cli/php_cli.c#L1298

So either php is being invoked without that argument (which is unlikely), or php cannot find the executable name given in argv[0]:

  • When you use the full path to the php executable, then php just checks if the name exists (which it does, because you are running it)
  • When you use composer the shell will resolve this to php and php itself will look in your PATH environment variable to find a match. If it cannot, then PHP_BINARY will be empty.

it is not standart php file: /usr/bin/php8.1 but all exists

So what is it then?

johnstevenson avatar May 10 '24 15:05 johnstevenson

I created script

#!/usr/bin/env php
<?php

echo "PHP_BINARY=".PHP_BINARY."\n";
echo "PHP_BINDIR=".PHP_BINDIR."\n";
echo "getenv('PHP_BINARY')=".getenv('PHP_BINARY')."\n";
echo "which php=".exec("which php")."\n";

when I run from console I get:

PHP_BINARY=/usr/bin/php8.1
PHP_BINDIR=/usr/bin
getenv('PHP_BINARY')=
which php=/usr/bin/php

when I run from webserver:

PHP_BINARY=
PHP_BINDIR=/usr/bin
getenv('PHP_BINARY')=
which php=/usr/bin/php

but if I set enviroment PHP_BINARY=/usr/bin/php8.1:

PHP_BINARY=
PHP_BINDIR=/usr/bin
getenv('PHP_BINARY')=/usr/bin/php8.1
which php=/usr/bin/php

PHP_BINARY is empty, you are right. But maybe which or custom env will be used for failback

akaDJon avatar May 11 '24 09:05 akaDJon

But maybe which or custom env will be used for failback

Sorry, but which could be a different php to the one you are running, as demonstrated by your console results:

PHP_BINARY=/usr/bin/php8.1
which php=/usr/bin/php

So if PHP_BINARY is empty we cannot do anything. If you want to use a custom env, then use it in your calling script.

I cannot actually reproduce your problem using a basic php-fpm/nginx setup. Also you haven't explained what you mean by "it is not standart php file: /usr/bin/php8.1".

I ran this script from the web server:

#server.php
$eol = PHP_SAPI === 'cli' ? "\n" : "<br/>";
echo 'Hello world from sapi: ', PHP_SAPI, $eol;
echo 'PHP_BINARY: ', PHP_BINARY, $eol;

$script = __DIR__.'/cli.php';
$output = [];
exec('php '.$script, $output);
echo $eol, implode($eol, $output), $eol;

which calls:

#cli.php
$eol = PHP_SAPI === 'cli' ? "\n" : "<br/>";
echo 'Hello world from sapi: ', PHP_SAPI, $eol;
echo 'PHP BINARY: ', PHP_BINARY, $eol;

giving the following output:

Hello world from sapi: fpm-fcgi
PHP_BINARY: /usr/local/sbin/php-fpm

Hello world from sapi: cli
PHP BINARY: /usr/local/bin/php

johnstevenson avatar May 11 '24 16:05 johnstevenson

So if PHP_BINARY is empty we cannot do anything. If you want to use a custom env, then use it in your calling script.

Yes, you are right

Also you haven't explained what you mean by "it is not standart php file: /usr/bin/php8.1"

I talked about /usr/sbin/php-fpm8.1 and I mean that /usr/bin/php8.1 is standart php bin file

I cannot actually reproduce your problem using a basic php-fpm/nginx setup

It's very simply:

server.php

<?php
$eol = PHP_SAPI === 'cli' ? "\n" : "<br/>";
echo 'Hello world from sapi: ', PHP_SAPI, $eol;
echo 'PHP_BINARY: ', PHP_BINARY, $eol;

$script = __DIR__.'/cli.php';
$output = [];
exec('php '.$script, $output);
echo $eol, implode($eol, $output), $eol;

       $descriptors = [
            0 => ['pipe', 'r'], // stdin
            1 => ['pipe', 'w'], // stdout
            2 => ['pipe', 'w']  // stderr
        ];

        $process = proc_open($script, $descriptors, $pipes);

        if (is_resource($process)) {
            fclose($pipes[0]);

            $output = stream_get_contents($pipes[1]);
            fclose($pipes[1]);

            $errors = stream_get_contents($pipes[2]);
            fclose($pipes[2]);

            $returnCode = proc_close($process);

            print_r("Output: $output");
            print_r("Errors: $errors");
            print_r("Return code: $returnCode");
        }

cli.php

#!/usr/bin/env php
<?php
$eol = PHP_SAPI === 'cli' ? "\n" : "<br/>";
echo 'Hello world from sapi: ', PHP_SAPI, $eol;
echo 'PHP BINARY: ', PHP_BINARY, $eol;

chmod +x ./cli.php

open http://127.0.0.1/server.php

akaDJon avatar May 12 '24 15:05 akaDJon