【问题标题】:PHP - how to best determine if the current invocation is from CLI or web server?PHP - 如何最好地确定当前调用是来自 CLI 还是 Web 服务器?
【发布时间】:2009-06-01 02:26:42
【问题描述】:

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

有什么推荐的方法吗?

【问题讨论】:

标签: php


【解决方案1】:

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

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

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

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

【讨论】:

  • 我做了一项研究:如果您使用php-cgi 调用脚本,这将不起作用。反过来,它将返回cgi-fcgi String。如果您从浏览器将脚本作为网页加载,您将获得apache2handler。希望这可以帮助。我需要使用php-cgi 来引入$_GET 变量:php-cgi myscript.php arg1=one arg2=two。测试不等于apache2handler 对于apache 应该没问题。
  • 关于此方法的一个小警告:当从 cron 作业运行时,它不会返回 "cli"$_SERVER 内部有许多不同的键可供选择,以更可靠地确定请求是否通过 HTTP 来。
  • @omninonsense 我用 PHP 7.2.7 测试过,它返回 cli。
【解决方案2】:

这个功能我用了好几年了

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

【讨论】:

  • 这个函数在我的情况下有效,而不是 php_sapi_name(),因为我需要区分 web 请求和 cronjob 执行,而 php_sapi_name() 结果在这两种情况下都是“php-cgi”。跨度>
  • 我能否信任所有这些变量,或者网络用户是否有可能通过欺骗其中一个变量来获得应仅限于 CLI 用户的访问权限?
【解决方案3】:

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

幸运的是,PHP 有办法专门检查这一点。只需使用不带任何参数的http_response_code(),如果从 Web 服务器类型环境运行,它将返回 TRUE,如果从 CLI 类型环境运行,它将返回 FALSE。代码如下:

$is_web=http_response_code()!==FALSE;

如果您在调用它之前不小心(?)从从 CLI(或类似 CLI 的东西)运行的脚本中设置了响应代码,这甚至会起作用。

【讨论】:

  • 当我回答这个问题时,这个问题已经很老了。投票这个答案可能比发布另一个重复的答案更有用,除非没有解释。
  • 关于“如果你不小心(?)在调用它之前从 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 是一个安全的选择,但如果不是,您还需要检查其他指标。
【解决方案4】:

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

列举几个取自 php docs on php_sapi_name():

  • aolserver
  • 阿帕奇
  • apache2filter
  • apache2handler
  • cgi (直到 PHP 5.3)
  • cgi-fcgi
  • cli
  • cli-server(Built-in web server 自 PHP 5.4 起)
  • 连续性
  • 嵌入
  • fpm-fcgi
  • isapi
  • 极速
  • 过滤器
  • nsapi
  • phttpd
  • pi3web
  • 罗克森
  • thttpd
  • 晚礼服
  • 网络游戏

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:
      function is_cli() {
          return !http_response_code();
      }
      

      示例:

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

      【讨论】:

      • 从手册中,“如果未提供 response_code 并且未在 Web 服务器环境中调用(例如从 CLI 应用程序),则将返回 FALSE。如果提供 response_code 并且它不会在 Web 服务器环境中调用(但仅在没有设置先前的响应状态时)”。你的逻辑倒退了。
      • +1。惊人的。经过这么多年毫无结果的研究......我特此授予您诺贝尔 PHPics 纪念奖,与 @krowe2 分享,以表彰他在实际工作中做出的宝贵贡献。恭喜!
      • 可能是某些东西设置了响应代码...http_response_code(200);...如果我现在调用http_response_code(),它将返回 200;
      • @BradKent 仅当您从 Web 环境中调用它时,只要您没有将状态代码设置为零(即无论如何都是无效的 HTTP 状态代码)。即使在这种情况下,我的版本也可以工作,因为它专门检查 FALSE。如果从 CLI 环境调用 http_response_code();,它将始终返回 FALSE,而不管实际状态代码如何。我已经在我的回答中解释了这一点,但您也可以通过阅读“返回值”下的手册页或尝试它来发现这一点。
      • @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)
      【解决方案7】:

      试试

      isset($_SERVER['REQUEST_METHOD'])
      

      如果已设置,则您在浏览器中。

      或者,您可以检查是否

      isset($_SERVER['argv'])
      

      但在 Windows CLI、IDK 上可能不是这样。

      【讨论】:

      • 虽然这不是“正确”的答案,但它可能更可靠
      【解决方案8】:

      我用过这个:

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

      这是来自 Drush 代码库 environment.inc,他们在那里进行了类似的检查。

      【讨论】:

      • 如果设置了register_argc_argv,那么传入任意数量的GET值都会导致argc不为0。
      【解决方案9】:

      根据http://jp2.php.net/manual/en/features.commandline.php,有一些常量仅在从 CLI 运行时设置。这些常量是 STDIN、STDOUT 和 STDERR。测试其中一个会告诉你它是否处于 cli 模式

      【讨论】:

        【解决方案10】:

        joomla 方式

        if (array_key_exists('REQUEST_METHOD', $_SERVER)) die();
        

        【讨论】:

          【解决方案11】:

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

          例如:

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

          【讨论】:

          • 这不适用于php-cgi 命令行,它会将其设置为GET for:php-cgi -f file.php arg1=2
          【解决方案12】:

          一种简单的方法是查询$argv 变量(无论如何,您可能会对命令行参数执行此操作)。即使没有参数$argv也返回一个空数组。

          如果已设置,则使用 cli。然后,您可以假设所有其他调用都是通过某个 Web 服务器或其他。

          例如:

          if (isset($argv)) {
            // Do the cli thing.
          }
          

          【讨论】:

          • 你不应该检查 $argv,因为 php-ini 定义了它是否被创建(仅在命令行上它总是打开,但它也可以在网络服务器上激活)
          【解决方案13】:

          这个问题的正确答案取决于它背后的真正意图:

          • SAPI 是否是决定因素(网络上下文与否)?
          • 或者信息被解释为“在 tty 中运行”?

          如果前者给出的答案和编写的 cmets 足以找到可行的解决方案。

          如果是后者,如果该工具作为 cronjob 或作为来自另一个守护程序的后台作业运行,此处给出的配方将失败 - 在这种情况下,我建议进一步测试 STDIN 是否是 TTY:

          function at_tty() {
              return defined("\STDIN") && posix_isatty(\STDIN);
          }
          

          【讨论】:

            【解决方案14】:
            // Detect CLI calls
            define("IS_CLI_CALL",( strcmp(php_sapi_name(),'cli') == 0 ));
            
            if(IS_CLI_CALL){
               //do you stuff here
            
            }
            

            【讨论】:

              【解决方案15】:

              基于Silver Moon's answer above,我正在使用此函数返回正确的换行符:

              /**
              * 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>";
              
              }
              

              【讨论】:

                【解决方案16】:

                如何,这么多复杂的解决方案。怎么样...

                if($_SERVER['REQUEST_SCHEME']=="http" or $_SERVER['REQUEST_SCHEME']=="https"){
                    // must be browser :)
                }
                

                【讨论】:

                • “不能保证每个 Web 服务器都会提供其中的任何一个;服务器可能会省略一些,或者提供其他未在此处列出的。” (php.net/manual/en/reserved.variables.server.php) 你不能保证 REQUEST_SCHEME 会出现。因此,如果它是网络请求,您的方法可能会出现误报。
                【解决方案17】:

                实用提示

                官方的方式(正如许多人所说的)是将PHP_SAPI 作为常量,或将php_sapi_name() 作为函数,当您处于命令行情况时,它们都返回cli。他们是对的。

                但是!...

                考虑使用$_SERVER["argv"](在大多数情况下也是$argv),当您在浏览器中运行时为null,当您从命令行调用时使用数组。这种方法(或同时使用)的优点是您可以在浏览器中模拟终端运行,只需为 $argv / $_SERVER["argv"] 变量提供(假)值.当您在通常无法获得 SSH 访问权限的外部服务器(prod、staging 等)上进行测试时,这会派上用场。

                最好的方法是记住你是否需要 CLI 模拟,并使用 $argvPHP_SAPI 来协调这个 - 例如如果 PHP_SAPI 不是 "c​​li" 但 $argv 有值,您可能需要预先输出一个额外的 &lt;pre&gt; 标签。

                【讨论】:

                  【解决方案18】:

                  我的首选方法:

                  if (array_key_exists('SHELL', $_ENV)) {
                    echo "Console invocation";
                  }
                  else {
                    echo "HTTP invocation";
                  }
                  

                  【讨论】:

                  • 环境变量 SHELL 仅存在于 Linux/Unix 上,所以这不是检查的好方法
                  【解决方案19】:

                  我会尝试:

                  echo exec('whoami');
                  

                  通常网络服务器在不同的用户名下运行,所以这应该能说明问题。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-06-15
                    • 2019-08-02
                    • 2015-05-24
                    • 1970-01-01
                    • 2012-11-10
                    相关资源
                    最近更新 更多