26

I want to run a PHP CLI program from within the PHP CLI. On some machines where this will run, both PHP 4 and PHP 5 are installed. If I run the outer program as

php5 outer.php

I want the inner script to be run with the same PHP version. In Perl, I would use $^X to get the Perl executable. It appears there isn't any such variable in PHP.

Right now, I'm using $_SERVER['_'], because Bash (and zsh) sets the environment variable $_ to the last-run program. But, I'd rather not rely on a shell-specific idiom.

UPDATE: Version differences are but one problem. If PHP isn't in PATH, for example, or isn't the first version found in PATH, the suggestions to find the version information won't help.

Additionally, csh and variants appear to not set the $_ environment variable for their processes, so the workaround isn't applicable there.

UPDATE 2: I was using $_SERVER['_'], until I discovered that it doesn't do the right thing under xargs (which makes sense... zsh sets it to the command it ran, which is xargs, not php5, and xargs doesn't change the variable). I am falling back to using:

$version = explode('.', phpversion());
$phpcli = "php{$version[0]}";
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
benizi
  • 4,046
  • 5
  • 28
  • 25
  • Is it possible to simply conditionally include the other PHP file? That's the easiest way to insure the included code runs with the same interpreter as the including code. – user229044 Mar 03 '10 at 19:58
  • Interesting point. In this particular case, part of the reason it's being run as a subprocess is that the inner script calls 'exit()' at various places. – benizi Mar 04 '10 at 00:15

9 Answers9

39

It is worth noting that now in PHP 5.4+ you can use the predefined constant PHP_BINARY:

PHP_BINARY

Specifies the PHP binary path during script execution. Available since PHP 5.4.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kristopher Ives
  • 5,838
  • 7
  • 42
  • 67
  • 2
    Note: in Apache2handler return `httpd` path. – Protomen Apr 05 '15 at 02:41
  • note: it returns the `php-cgi` and not `php` in my case (windows) – ThaDafinser Dec 03 '15 at 15:49
  • 1
    This returns `C:\php` on Windows where no such file or directory exists (PHP 7.1.11, Apache 2.4.29, Windows 7 Pro) - as, as noted in a comment on another answer, does `PHP_BINDIR`. – Jake Dec 16 '17 at 23:19
  • And on the hosting company's Linux box it also returns the same as `PHP_BINDIR`. Here it is `/usr/bin` and does contain a `php` executable, but that is the version 5.3 executable not the 7.0.25 version of the calling environment. So I don't think this answer is useful. – Jake Dec 16 '17 at 23:46
  • @Jake How are you testing that? – Kristopher Ives Dec 18 '17 at 05:00
  • @KristopherIves Via HTTP and Apache. I must have actually been using `PHP_BINDIR` rather than `PHP_BINARY` when testing before (hence why the same values). With `PHP_BINARY` I get: Windows 7 32-bit / PHP 7.1.11 - `httpd.exe`; Hosting Co. 1 (Linux / PHP 7.0.25) - `""` (empty string); Hosting Co. 2 (Linux / PHP 7.1.7) - `.../php-cgi` (correct path for version). So on only one of 3 systems is it giving the correct value. – Jake Dec 19 '17 at 22:08
  • 1
    For php-fpm (on Debian) this returns `/usr/sbin/php-frpm7.3` which cannot be used to launch any PHP script. While technically correct, it's completely useless for OP's purpose. – jlh May 17 '21 at 12:55
8

On my server I've PHP 5.3.14.

I've found a predefined constant: PHP_BIN_DIR

Then, supposing the file name of the executable file is always 'php', $php_cmd = PHP_BIN_DIR.'/php' points to my PHP executable file.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 5
    [According to the manual](http://www.php.net/manual/en/reserved.constants.php), which doesn't say when it was added, it's `PHP_BINDIR` (no second underscore). This is a pretty decent answer. Thanks. – benizi Jun 26 '12 at 03:33
  • 10
    I upvoted the answer and the comment but took the upvotes back after trying it in Windows. Will always return `C:\php` no matter the reality. – Camilo Martin Apr 25 '13 at 21:27
  • Nice answer, however, it won't work as expected if multiple versions of PHP are installed on a Linux system, since they will likely all be in `/usr/bin`. – jlh May 17 '21 at 12:59
5

Update; nowadays PHP_BINARY works with XAMPP as well (Tested with XAMPP that comes with PHP 7.4).


Old Answer

Unfortunately PHP_BINARY is returning the httpd binary (on Windows XAMPP), so I'm back to using paths...

    if (defined('PHP_BINARY') &&
                PHP_BINARY &&
                in_array(PHP_SAPI, array('cli', 'cli-server')) &&
                is_file(PHP_BINARY)) {

          return PHP_BINARY;
    } else if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        $paths = explode(PATH_SEPARATOR, getenv('PATH'));
        foreach ($paths as $path) {
            if (substr($path, strlen($path)-1) == DIRECTORY_SEPARATOR) {
                $path = substr($path, 0, strlen($path)-1);
            }
            if (substr($path, strlen($path) - strlen('php')) == 'php') {
                $response = $path.DIRECTORY_SEPARATOR . 'php.exe';
                if (is_file($response)) {
                    return $response;
                }
            } else if (substr($path, strlen($path) - strlen('php.exe')) == 'php.exe') {
                if (is_file($response)) {
                    return $response;
                }
            }
        }
    } else {
        $paths = explode(PATH_SEPARATOR, getenv('PATH'));
        foreach ($paths as $path) {
            if (substr($path, strlen($path)-1) == DIRECTORY_SEPARATOR) {
                $path = substr($path, strlen($path)-1);
            }
            if (substr($path, strlen($path) - strlen('php')) == 'php') {
                if (is_file($path)) {
                    return $path;
                }
                $response = $path.DIRECTORY_SEPARATOR . 'php';
                if (is_file($response)) {
                    return $response;
                }
            }
        }
    }
    return null;
Top-Master
  • 7,611
  • 5
  • 39
  • 71
ZipXap
  • 51
  • 1
  • 2
  • 1
    Has someone tried it? Does it work? Consistently and across Win and Unix? The code is ugly, but if it works, I'll normalize it. – XedinUnknown Oct 23 '15 at 11:52
  • 1
    I know this is an old answer an no one has answers @XedinUnknown but I can confirm this works perfectly on Windows running XAMPP. Has anyone else tested this in more environments? – Blizzardengle Jan 21 '18 at 05:03
3

Okay, so this is ugly, but it works on Linux:

<?php
    // Returns the full path of the current PHP executable
    function get_proc_name(){
       // Gets the PID of the current executable
       $pid = posix_getpid();

       // Returns the exact path to the PHP executable.
       $exe = exec("readlink -f /proc/$pid/exe");
       return $exe;
    }

It doesn't look like there's any easy way to do this for Windows. Some Windows executables like tasklist can give you the name of the executable, but not the full path to where the executable is. I was only able to find examples for finding the full path given a PID for C++, AutoHotkey and the like. If anyone has any suggestions on where else I could look, let me know.

PS: To get the PID in PHP for Windows, you apparently have to call getmypid().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cmptrgeekken
  • 8,052
  • 3
  • 29
  • 35
  • I was looking into doing this too. I thought I might be able to use the [w32api](http://pecl.php.net/package/w32api) or [ffi](http://pecl.php.net/package/ffi) PECL extensions to call the [GetModuleFileName](http://msdn.microsoft.com/en-us/library/ms683197%28v=vs.85%29.aspx) API function, but neither extension seems to be actively maintained at the moment. – Ken Keenan Aug 20 '11 at 08:25
  • 3
    Instead of faffing with `$pid` you can always just look at `"/proc/self/exe"` – Ash Berlin-Taylor Sep 19 '11 at 16:39
  • I love this solution btw ! – Kristopher Ives Jun 25 '14 at 19:33
2

You could use phpversion() to get the current version of PHP before you execute the "inner" script.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Richard JP Le Guen
  • 28,364
  • 7
  • 89
  • 119
0

After being dissatisfied with the answers, I came up with my own. It tries to use PHP_BINARY if it looks reasonable, otherwise it looks for an interpreter with the same version as the currently running one.

/**
 * Return a suitable PHP interpreter that is likely to be the same version as the
 * currently running interpreter.  This is similar to using the PHP_BINARY constant, but
 * it will also work from within mod_php or PHP-FPM, in which case PHP_BINARY will return
 * unusable interpreters.
 *
 * @return string
 */
public function getPhpInterpreter(): string {
    static $cachedExecutable = null;

    if ($cachedExecutable !== null) {
        return $cachedExecutable;
    }

    $basename = basename(PHP_BINARY);

    // If the binary is 'php', 'php7', 'php7.3' etc, then assume it's a usable interpreter
    if ($basename === 'php' || preg_match('/^php\d+(?:\.\d+)*$/', $basename)) {
        return PHP_BINARY;
    }

    // Otherwise, we might be running as mod_php, php-fpm, etc, where PHP_BINARY is not a
    // usable PHP interpreter.  Try to find one with the same version as the current one.

    $candidates = [
        'php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION,
        'php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
        'php' . PHP_MAJOR_VERSION,
    ];

    $envPath = $_SERVER['PATH'] ?? '';
    $paths = $envPath !== '' ? explode(':', $envPath) : [];

    if (!in_array(PHP_BINDIR, $paths, true)) {
        $paths[] = PHP_BINDIR;
    }

    foreach ($candidates as $candidate) {
        foreach ($paths as $path) {
            $executable = $path . DIRECTORY_SEPARATOR . $candidate;
            if (is_executable($executable)) {
                $cachedExecutable = $executable;
                return $executable;
            }
        }
    }

    // Fallback, if nothing else can be found
    $cachedExecutable = 'php';
    return $cachedExecutable;
}

Let me know if this works for you. I tested it on Debian Buster, from CLI and from FPM.

jlh
  • 4,349
  • 40
  • 45
-1

Unfortunately I can't find a shorter way, but findPHP() works pretty well and is multi OS/PHP version compatible.

$lookIn could probably be extended to include more common locations.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
CJSewell
  • 183
  • 2
  • 8
-2

You can try and parse the phpinfo() result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Itay Moav -Malimovka
  • 52,579
  • 61
  • 190
  • 278
-3

I think the best constant is PHP_BINARY. With PHP 5.5.12.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Guy Bami
  • 25
  • 2
  • Looks identical to the [accepted answer](http://stackoverflow.com/questions/2372624/get-current-php-executable-from-within-script/9182368#9182368). – Pang May 08 '15 at 03:45