PHP - 如何最好地确定当前调用是来自CLI还是Web服务器?

226

我需要确定当前 PHP 的调用是来自命令行 (CLI) 还是来自 Web 服务器 (在我的情况下,是带有 mod_php 的 Apache)。

是否有任何推荐的方法?


你可以使用这个库:https://github.com/arcanisgk/WEB-CLI-Detector - user17161735
20个回答

357

php_sapi_name是你希望使用的函数,因为它返回接口类型的小写字符串。此外,还有PHP常量PHP_SAPI

文档可以在这里找到:http://php.net/php_sapi_name

例如,要确定是否从CLI运行PHP,您可以使用此函数:

function isCommandLineInterface()
{
    return (php_sapi_name() === 'cli');
}

11
我进行了一项研究:如果你使用 php-cgi 命令调用这个脚本,它是不起作用的。反而会返回 cgi-fcgi 字符串。如果你从浏览器中加载这个脚本作为一个网页,你会得到 apache2handler。希望这可以帮到你。我需要使用 php-cgi 来引入 $_GET 变量:php-cgi myscript.php arg1=one arg2=two。测试是否不等于 apache2handler 应该对 apache 是可行的。 - Sebastian
4
这种方法有一个小注意点:当在cron job中运行时,它不会返回"cli"。在$_SERVER内部有许多不同的键可供选择,以更可靠地确定请求是通过HTTP还是其他方式发出的。 - omninonsense
4
我测试了 PHP 7.2.7 并返回 cli。 - Jose Nobile
你可以使用这个库:https://github.com/arcanisgk/WEB-CLI-Detector - user17161735

48

我已经使用这个函数几年了

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;
}

1
这个函数在我的情况下起作用了,而不是php_sapi_name(),因为我需要区分Web请求和cronjob执行,并且php_sapi_name()的结果在两种情况下都是"php-cgi"。 - luis.ap.uyen
3
我能相信所有这些变量吗?还是有可能一个网络用户伪造其中一个变量以获取应该限制给CLI用户的访问权限? - Benedikt

38

php_sapi_name() 不是最好的方法来执行此检查,因为它要检查许多可能的值。php-cgi二进制文件可以从命令行、shell脚本或计划任务中调用,并且(在大多数情况下)这些应该也被视为'cli',但是对于这些情况,php_sapi_name() 将返回不同的值(注意,对于PHP的普通版本不是这种情况,但是您希望您的代码可以在任何地方使用,对吗?)。更不用说明年可能会有新的使用PHP的方式,我们现在无法知道。当我只关心是否应该将输出包装在HTML中时,我宁愿不去考虑它。

幸运的是,PHP有一种特定的方法来检查这个。只需要使用http_response_code(),不带任何参数,如果从Web服务器类型环境运行,则返回TRUE,如果从CLI类型环境运行,则返回FALSE。以下是代码:

$is_web=http_response_code()!==FALSE;

如果您在从命令行(或类似于命令行的地方)运行的脚本中调用此函数之前意外设置了响应代码,它甚至也能正常工作。


5
当我回答这个问题时,它已经很旧了。点赞这个答案可能比发另一个没有解释的重复答案更有用。 - krowe2
3
关于“即使您在从CLI运行的脚本中意外(?)设置了响应代码,这也将起作用([...])在调用此之前。”:至少在PHP 7.4.3中,这是不正确的。http_response_code()在从CLI运行时设置代码/返回设置代码。通过<?php function t() { echo var_export(http_response_code(), true) . ' -> ' . (http_response_code() !== false ? 'web' : 'cli') . "\n"; } t(); http_response_code(200); t(); http_response_code(false); t();进行验证。因此,如果http_response_code()===false,那么可以放心地假设是CLI,但如果不是,则还需要检查其他指标。 - Sebastian B.

24

我认为他的意思是PHP CLI是否被调用或者是来自Web请求的响应。最好的方法是使用php_sapi_name(),如果正在运行Web请求,它将输出Apache。

以下是从PHP文档中提取的一些:

  • aolserver
  • apache
  • apache2filter
  • apache2handler
  • caudium
  • cgi (直到PHP 5.3)
  • cgi-fcgi
  • cli
  • cli-server (作为PHP 5.4内置的Web服务器)
  • continuity
  • embed
  • fpm-fcgi
  • isapi
  • litespeed
  • milter
  • nsapi
  • phttpd
  • pi3web
  • roxen
  • thttpd
  • tux
  • webjames

13

这应该处理所有情况(包括php-cgi)

return (php_sapi_name() === 'cli' OR defined('STDIN'));

8
function is_cli() {
    return !http_response_code();
}

例子:

if (is_cli()) {
    echo 'command line';
} else {
    echo 'browser';
}

2
从手册中可以看到,“如果未提供response_code并且它未在Web服务器环境(例如从CLI应用程序)中调用,则返回FALSE。如果提供了response_code并且它未在Web服务器环境中调用(但仅当没有设置先前的响应状态时),则返回TRUE。”你的逻辑是相反的。 - krowe2
1
+1。太棒了。经过这么多年的徒劳研究...我在此授予您与@krowe2共同获得PHPics诺贝尔纪念奖,以表彰他对使其实际运作做出的宝贵贡献。恭喜! - Sz.
有可能某些东西已经设置了响应代码... http_response_code(200);... 如果我现在调用 http_response_code(),它将返回 200。 - Brad Kent
@BradKent 只有在Web环境中调用此函数时才会出现这种情况,即使如此,只要您没有将状态代码设置为零(这是无效的HTTP状态代码),此检查仍将起作用。我的版本即使在这种情况下也会起作用,因为它专门针对FALSE进行检查。如果从CLI环境中调用http_response_code();,则无论实际状态代码如何,它始终会返回FALSE。我已经在我的答案中解释了这一点,但您也可以通过阅读“返回值”下的手册页面或尝试来找到这个问题的答案。 - krowe2
@krowe2 php -r 'http_response_code(200); echo http_response_code()."\n";' 输出 "200"。除非您能保证某个库或无知的框架没有使用 http_response_code 设置响应代码(即使在 cli 环境中),否则这将起作用。就我个人而言,我使用 $isCli = \defined('STDIN') || isset($_SERVER['argv']) || \array_key_exists('REQUEST_METHOD', $_SERVER) - Brad Kent
当在控制台中使用 php-cgi 时,这也会返回 200 - vanowm

7

尝试

isset($_SERVER['REQUEST_METHOD'])

如果设置了,你就在浏览器中。
或者,你可以检查
isset($_SERVER['argv'])

但这可能在Windows CLI上并不正确,我不确定。


1
虽然这不是“正确”的答案,但它可能更可靠。 - challet

3
我用了这个:
php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)

这段代码来自于Drush的代码库,environment.inc文件中有类似的检查。


4
如果设置了register_argc_argv,则传递任意数量的GET值将导致argc不为0。 - user3477804

2
我建议检查 $_SERVER 数组的一些条目是否已设置。

E.g.:

if (isset($_SERVER['REQUEST_METHOD'])) {
        print "HTTP request\n";
} else {
        print "CLI invocation\n";
}

这个命令不适用于 php-cgi 命令行,它会将其设置为 GET,例如:php-cgi -f file.php arg1=2 - Sebastian
1
@sebastian,它在Windows上可以使用PHP 8.0.9。 - vanowm

2
Joomla 方式
if (array_key_exists('REQUEST_METHOD', $_SERVER)) die();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接