226

I need to determine whether the current invocation of PHP is from the command line (CLI) or from the web server (in my case, Apache with mod_php).

Any recommended methods?

Joe Lencioni
  • 10,231
  • 18
  • 55
  • 66
Shameem
  • 14,199
  • 13
  • 40
  • 43
  • 2
    Possible duplicate of [What is the canonical way to determine commandline vs. http execution of a PHP script?](https://stackoverflow.com/questions/173851/what-is-the-canonical-way-to-determine-commandline-vs-http-execution-of-a-php-s) –  Jul 15 '18 at 10:30
  • you can use this library: https://github.com/arcanisgk/WEB-CLI-Detector –  Aug 01 '22 at 17:58

19 Answers19

355

php_sapi_name is the function you will want to use as it returns a lowercase string of the interface type. In addition, there is the PHP constant PHP_SAPI.

Documentation can be found here: http://php.net/php_sapi_name

For example, to determine if PHP is being run from the CLI, you could use this function:

function isCommandLineInterface()
{
    return (php_sapi_name() === 'cli');
}
Nathan J.B.
  • 10,215
  • 3
  • 33
  • 41
Jordan S. Jones
  • 13,703
  • 5
  • 44
  • 49
  • 11
    I did a research: if you invoke the script with `php-cgi` this won't work. In turn, it will return `cgi-fcgi` String. If you load the script as a web page from a browser, you'll get `apache2handler`. Hope this helps. I needed to use `php-cgi` in order to introduce `$_GET` variables: `php-cgi myscript.php arg1=one arg2=two`. Testing for not equal to `apache2handler` should be ok for `apache`. – Sebastian Jun 12 '13 at 01:01
  • 3
    A small caveat about this method: it will *not* return `"cli"` when run from a cron job. There's a number od differents keys to choose from inside of `$_SERVER` to determine more reliably whether the request came via HTTP or not. – omninonsense Jun 03 '16 at 08:00
  • 4
    @omninonsense I tested with PHP 7.2.7 and it returns cli. – Jose Nobile Jul 01 '18 at 17:00
  • you can use this library: https://github.com/arcanisgk/WEB-CLI-Detector –  Aug 01 '22 at 18:01
47

I have been using this function for a few years

function is_cli()
{
    if ( defined('STDIN') )
    {
        return true;
    }

    if ( php_sapi_name() === 'cli' )
    {
        return true;
    }

    if ( array_key_exists('SHELL', $_ENV) ) {
        return true;
    }

    if ( empty($_SERVER['REMOTE_ADDR']) and !isset($_SERVER['HTTP_USER_AGENT']) and count($_SERVER['argv']) > 0) 
    {
        return true;
    } 

    if ( !array_key_exists('REQUEST_METHOD', $_SERVER) )
    {
        return true;
    }

    return false;
}
pmaruszczyk
  • 2,157
  • 2
  • 24
  • 49
Silver Moon
  • 1,086
  • 14
  • 19
  • 1
    This function worked in my case, not php_sapi_name(), since I needed to distinguish between web requests and cronjob executions, and the php_sapi_name() result was "php-cgi" in both cases. – luis.ap.uyen Jun 06 '18 at 10:37
  • 3
    Can I trust all these variables or is there a possibility a web user could spoof one of those in order to get access that should be limited to users on CLI? – Benedikt Nov 21 '21 at 11:41
37

php_sapi_name() is really not the best way to perform this check because it depends on checking against many possible values. The php-cgi binary can be called from the command line, from a shell script or as a cron job and (in most cases) these should also be treated as 'cli' but php_sapi_name() will return different values for these (note that this isn't the case with the plain version of PHP but you want your code to work anywhere, right?). Not to mention that next year there may be new ways to use PHP that we can't possibly know now. I'd rather not think about it when all I care about is weather I should wrap my output in HTML or not.

Fortunately, PHP has a way to check for this specifically. Just use http_response_code() without any parameters and it'll return TRUE if ran from a web server type environment and FALSE if ran from a CLI type environment. Here is the code:

$is_web=http_response_code()!==FALSE;

This will even work if you accidentally(?) set a response code from a script running from the CLI (or something like the CLI) before you call this.

robsch
  • 9,358
  • 9
  • 63
  • 104
krowe2
  • 227
  • 4
  • 12
  • 5
    This question was already old when I answered it. Up-voting this answer would've probably been more useful than posting another answer that is a duplicate except without the explanation. – krowe2 Feb 15 '17 at 15:49
  • 2
    Regarding "This will even work if you accidentally(?) set a response code from a script running from the CLI ([...]) before you call this.": (at least) as of PHP 7.4.3, this is not true. `http_response_code()` sets code/returns set code when running from CLI. Verified by ` ' . (http_response_code() !== false ? 'web' : 'cli') . "\n"; } t(); http_response_code(200); t(); http_response_code(false); t();`. So if `http_response_code()===false` then it`s a safe bet to assume CLI, but if not you need to check also other metrics. – Sebastian B. May 28 '20 at 07:20
24

I think he means if PHP CLI is being invoked or if it is a response from a web request. The best way would be to use php_sapi_name() which if it was running a web request would echo Apache if that is what it was running.

To list of a few taken from the php docs on php_sapi_name():

  • aolserver
  • apache
  • apache2filter
  • apache2handler
  • caudium
  • cgi (until PHP 5.3)
  • cgi-fcgi
  • cli
  • cli-server (Built-in web server as of PHP 5.4)
  • continuity
  • embed
  • fpm-fcgi
  • isapi
  • litespeed
  • milter
  • nsapi
  • phttpd
  • pi3web
  • roxen
  • thttpd
  • tux
  • webjames
Marc Towler
  • 705
  • 11
  • 32
13

This should handle all the cases (including php-cgi)

return (php_sapi_name() === 'cli' OR defined('STDIN'));
rbawaskar
  • 1,035
  • 2
  • 10
  • 25
8
function is_cli() {
    return !http_response_code();
}

example:

if (is_cli()) {
    echo 'command line';
} else {
    echo 'browser';
}
Sz.
  • 3,342
  • 1
  • 30
  • 43
Terry Lin
  • 2,529
  • 22
  • 21
  • 2
    From the manual, "FALSE will be returned if response_code is not provided and it is not invoked in a web server environment (such as from a CLI application). TRUE will be returned if response_code is provided and it is not invoked in a web server environment (but only when no previous response status has been set)". You've gotten your logic backwards. – krowe2 Feb 15 '17 at 16:04
  • 1
    +1. Amazing. After so many years of fruitless research... I hereby award you the Nobel Memorial Prize in PHPics, shared with @krowe2 for his invaluable contribution of actually making it work. Congratulations! – Sz. Feb 15 '17 at 18:14
  • It's possible that something may have set the response code... `http_response_code(200);`... if I now call `http_response_code()` it will return 200; – Brad Kent Mar 02 '19 at 02:01
  • @BradKent That is ONLY if you call this from a web environment and, in that case, this check is still going to work as long as you didn't set the status code to zero (which is an invalid HTTP status code anyway). My version will work even even in that case because it checks against FALSE specifically. If `http_response_code();` is called from a CLI environment it will always return FALSE regardless of the actual status code. I've already explained this in my answer but you could've also found this out by reading the manual page under "Return Values" or by trying it. – krowe2 Apr 03 '19 at 15:11
  • @krowe2 `php -r 'http_response_code(200); echo http_response_code()."\n";'` outputs "200" Unless you can guarantee that some library or ignorant framework hasn't set the response code with `http_response_code` (even in a cli env), then this will work. Personally, I use `$isCli = \defined('STDIN') || isset($_SERVER['argv']) || \array_key_exists('REQUEST_METHOD', $_SERVER)` – Brad Kent Apr 03 '19 at 18:53
  • This also returns `200` when used `php-cgi` in console – vanowm Dec 21 '22 at 01:02
7

Try

isset($_SERVER['REQUEST_METHOD'])

if it's set, you're in a browser.

Alternatlely, you could check if

isset($_SERVER['argv'])

but that might not be true on windows CLI, IDK.

gnud
  • 77,584
  • 5
  • 64
  • 78
3

I used this:

php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)

This is from Drush codebase, environment.inc where they have similar check to make.

Ranjan
  • 448
  • 3
  • 13
  • 4
    If `register_argc_argv` is set, then passing in any amount of GET values will cause `argc` to not be 0. –  Jul 17 '14 at 08:23
2

joomla way

if (array_key_exists('REQUEST_METHOD', $_SERVER)) die();
Sander
  • 1
  • 1
2

I would suggest to check if some of the entries of the $_SERVER array are set.

E.g.:

if (isset($_SERVER['REQUEST_METHOD'])) {
        print "HTTP request\n";
} else {
        print "CLI invocation\n";
}
rodion
  • 6,087
  • 4
  • 24
  • 29
2

According to http://jp2.php.net/manual/en/features.commandline.php There are a number of constants set only when running from the CLI. These constants are STDIN, STDOUT and STDERR. Testing for one of those will tell you if it is in cli mode

Jonathan Fingland
  • 56,385
  • 11
  • 85
  • 79
1

An easy way is to interrogate the $argv variable, (Which you will probably do for command line parameters anyway). Even if there are no parameters $argv returns an empty array.

If it is set, then cli was used. You may then assume all other invocations are via some web server or other.

eg:

if (isset($argv)) {
  // Do the cli thing.
}
lucsan
  • 712
  • 9
  • 10
  • You should not check for $argv because the php-ini defines if it is created or not (only on commandline it is ALWAYS on, but it also can be activated on webserver) – Radon8472 Nov 26 '21 at 08:15
1

The correct answer to this question depends on the real intent behind it:

  • Is the SAPI the deciding factor (web-context or not)?
  • Or is the information interpreted as 'running in a tty'?

If the former the answers given and comments written are enough to find a solution that works.

If the latter, the recipes given here will fail if the tool is run as cronjob, or as background-job from another daemon -- in that case I suggest to further test if STDIN is a TTY:

function at_tty() {
    return defined("\STDIN") && posix_isatty(\STDIN);
}
Tom Regner
  • 6,856
  • 4
  • 32
  • 47
1

How, so many complicated solutions. How about ...

if($_SERVER['REQUEST_SCHEME']=="http" or $_SERVER['REQUEST_SCHEME']=="https"){
    // must be browser :)
}
adrianTNT
  • 3,671
  • 5
  • 29
  • 35
  • "There is no guarantee that every web server will provide any of these; servers may omit some, or provide others not listed here." (https://www.php.net/manual/en/reserved.variables.server.php) You cannot guarantee that REQUEST_SCHEME will be present. So your method could have false negatives for testing if it's a web request. – ADJenks Mar 25 '22 at 22:36
0
// Detect CLI calls
define("IS_CLI_CALL",( strcmp(php_sapi_name(),'cli') == 0 ));

if(IS_CLI_CALL){
   //do you stuff here

}
Hắc Huyền Minh
  • 1,025
  • 10
  • 13
0

Based off Silver Moon's answer above, I'm using this function for returning correct linebreaks:

/**
* Linebreak function
* @return "/n" if cli, else return <br>
*/
protected static function lb(){

    return (defined('STDIN') || php_sapi_name() === 'cli' || isset($_ENV['SHELL']) ||
    (empty($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['HTTP_USER_AGENT']) && count($_SERVER['argv']) > 0) ||
    !isset($_SERVER['REQUEST_METHOD'])) ? "\n" : "<br>";

}
a20
  • 5,495
  • 2
  • 30
  • 27
0

A practical hint

The official way (as told by many) is PHP_SAPI as a constant, or php_sapi_name() as a function, they both return cli when you're in a command line situation. They're right.

But!...

Consider using $_SERVER["argv"] (also $argv in most cases) which is null when you run in a browser, and an array when you've been called from command line. The advantage of this approach (or using both) is that you can simulate a terminal run in a browser, by just giving a (fake) value to the $argv / $_SERVER["argv"] variable. This comes in handy when you test on an outside server (prod, staging, etc) where you typically won't get SSH access.

The best way to do this is keeping in mind whether you may or may not need a CLI simulation, and use both $argv and PHP_SAPI to coordinate this - e.g. you may need to output an extra <pre> tag beforehand if PHP_SAPI is not "cli" but $argv has a value.

dkellner
  • 8,726
  • 2
  • 49
  • 47
-1

My preferred method:

if (array_key_exists('SHELL', $_ENV)) {
  echo "Console invocation";
}
else {
  echo "HTTP invocation";
}
Travis Beale
  • 5,534
  • 7
  • 34
  • 34
-20

I'd try:

echo exec('whoami');

Usually webservers are run under a different username, so that should be telling.

Stefan Mai
  • 23,367
  • 6
  • 55
  • 61