PHP - как наилучшим образом определить, является ли текущий вызов из CLI или веб-сервера?


192

Мне нужно определить, является ли текущий вызов PHP из командной строки (CLI) или с веб-сервера (в моем случае, Apache с mod_php).

Любые рекомендуемые методы?


отредактировал и уточнил вопрос.
Shameem

Ответы:


308

php_sapi_nameэто функция, которую вы хотите использовать, поскольку она возвращает строчную строку типа интерфейса. Кроме того, есть константа PHP PHP_SAPI.

Документацию можно найти здесь: http://php.net/php_sapi_name

Например, чтобы определить, запускается ли PHP из CLI, вы можете использовать эту функцию:

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

11
Более просто:return php_sapi_name() == 'cli';
Savageman

11
Я провел исследование: если вы вызовете сценарий с php-cgiэтим, это не сработает. В свою очередь, он вернет cgi-fcgiString. Если вы загрузите скрипт как веб-страницу из браузера, вы получите apache2handler. Надеюсь это поможет. Мне нужно было использовать php-cgi, чтобы представить$_GET переменные: php-cgi myscript.php arg1=one arg2=two. Тестирование на не равно apache2handlerдолжно быть в порядке apache.
Себастьян

3
Небольшое предостережение об этом методе: не вернется "cli"при запуске из задания cron. Существует несколько различных ключей, которые можно выбрать изнутри, $_SERVERчтобы более надежно определить, поступил ли запрос по HTTP или нет.
omninonsense

2
@omninonsense Я тестировал с PHP 7.2.7, и он возвращает cli.
Хосе Нобиле

38

Я использую эту функцию в течение нескольких лет

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

10
array_key_exists('REQUEST_METHOD', $_SERVER) return true;WAT?
biziclop

@biziclop, проверка array_key_exists('REQUEST_METHOD', $_SERVER)правильна, чтобы помочь определить источник запроса . Когда в CLI , то $_SERVERсупер Global массив НЕ ИМЕЕТ ключ REQUEST_METHOD, он существует только тогда , когда делается запрос через Интернет, следовательно, автор абсолютно на цель при его проверке.
Хулио Марчи

1
@JulioMarchi: но не стоит ли это return false;делать if ( ! array_key_exists(…)) return true;?
Бизиклоп

2
@biziclop, ты совершенно прав !!!! Как я мог пропустить такую огромную маленькую деталь ??? Мне стыдно... :). Конечно, если ключ 'REQUEST_METHOD'будет НЕ найден, то функция должна возвращать FALSEуказать «CLI». Приношу свои извинения за то, что не обратил внимания на область действия самой функции ... Автор должен исправить это, потому что функция действительно работает!
Хулио Марчи

В моем случае эта функция работала, а не php_sapi_name (), поскольку мне нужно было различать веб-запросы и выполнения cronjob, а результатом php_sapi_name () в обоих случаях был "php-cgi".
luis.ap.uyen

36

php_sapi_name()действительно не лучший способ выполнить эту проверку, потому что это зависит от проверки на множество возможных значений. Бинарный файл php-cgi может быть вызван из командной строки, из сценария оболочки или как задание cron, и (в большинстве случаев) их также следует рассматривать как 'cli', ноphp_sapi_name() будут возвращаться разные значения (обратите внимание, что это не в случае простой версии PHP, но вы хотите, чтобы ваш код работал где угодно, верно?). Не говоря уже о том, что в следующем году могут появиться новые способы использования PHP, о которых мы не можем знать сейчас. Я бы предпочел не думать об этом, когда все, что меня волнует, это погода, я должен обернуть вывод в HTML или нет.

К счастью, в PHP есть способ проверить это специально. Просто используйте http_response_code()без каких-либо параметров, и он вернет TRUE, если он запускается из среды типа веб-сервера, и FALSE, если он запускается из среды типа CLI. Вот код:

$is_web=http_response_code()!==FALSE;

Это даже будет работать, если вы случайно (?) Установили код ответа из скрипта, запущенного из CLI (или что-то вроде CLI), прежде чем вызывать это.


4
Этот вопрос был уже стар, когда я ответил на него. Повторное голосование за этот ответ, вероятно, было бы более полезным, чем публикация другого ответа, который является дубликатом, кроме как без объяснения причин.
krowe2

Относительно «Это будет работать, даже если вы случайно (?) Установили код ответа из скрипта, запущенного из 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, но если нет, вам нужно проверить и другие метрики.
Себастьян Б.

23

Я думаю, он имеет в виду, вызывается ли PHP CLI или это ответ от веб-запроса. Лучшим способом было бы использовать, php_sapi_name()который, если бы он выполнял веб-запрос, повторил бы Apache, если это то, что он выполнял.

К списку нескольких взятых из документации поphp_sapi_name() php :

  • AOLserver
  • апаш
  • apache2filter
  • apache2handler
  • Caudium
  • CGI (до PHP 5.3)
  • CGI-FCGI
  • кли
  • cli-сервер ( встроенный веб-сервер с PHP 5.4)
  • непрерывность
  • встраивать
  • FPM-FCGI
  • ISAPI
  • LiteSpeed
  • рыба-самец
  • NSAPI
  • phttpd
  • pi3web
  • Roxen
  • thttpd
  • смокинг
  • webjames

13

Это должно обрабатывать все случаи (включая 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';
}

2
Из руководства «FALSE будет возвращено, если код ответа не предоставлен, и он не вызывается в среде веб-сервера (например, из приложения CLI). Значение TRUE будет возвращено, если предоставлен код ответа, и он не вызывается на веб-сервере. окружение (но только в том случае, если предыдущий статус ответа не был установлен) ". Вы получили свою логику в обратном направлении.
krowe2

1
+1. Удивительный. После стольких лет бесплодных исследований ... Настоящим награждаю вас Нобелевской мемориальной премией по PHPics, которой поделился с @ krowe2 за его неоценимый вклад в то, чтобы заставить его работать. Поздравляем!
СЗ

Возможно, что-то могло установить код ответа ... http_response_code(200);... если я сейчас позвоню, http_response_code()он вернет 200;
Брэд Кент

@BradKent Это ТОЛЬКО если вы вызываете это из веб-среды, и в этом случае эта проверка все еще будет работать, если вы не установили код состояния на ноль (который в любом случае является недействительным кодом состояния HTTP). Моя версия будет работать даже в этом случае, потому что она специально проверяет FALSE. Если http_response_code();вызывается из среды CLI, он всегда возвращает FALSE независимо от фактического кода состояния. Я уже объяснил это в своем ответе, но вы могли бы также узнать это, прочитав страницу руководства в разделе «Возвращаемые значения» или попробовав это.
krowe2

@ krowe2 php -r 'http_response_code(200); echo http_response_code()."\n";' выводит «200» Если вы не можете гарантировать, что какая-то библиотека или невежественный фреймворк не установит код ответа http_response_code(даже в среде), тогда это будет работать. Лично я использую$isCli = \defined('STDIN') || isset($_SERVER['argv']) || \array_key_exists('REQUEST_METHOD', $_SERVER)
Брэд Кент

4

Я использовал это:

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

Это из Drush codebase, environment.inc, где они должны выполнить аналогичную проверку.


4
Если register_argc_argvустановлено, то передача любого количества значений GET приведет argcк тому, что оно не будет равно 0.

3

Пытаться

isset($_SERVER['REQUEST_METHOD'])

если он установлен, вы в браузере.

Кроме того, вы можете проверить, если

isset($_SERVER['argv'])

но это может быть не так в Windows CLI, IDK.


1
Хотя это и не «правильный» ответ, он может быть более надежным
шале



0

Я бы посоветовал проверить, есть ли некоторые записи в $ _SERVER массива .

Например:

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

Это не будет работать с php-cgiкомандной строкой, оно будет установлено GETдля:php-cgi -f file.php arg1=2
Себастьян

0

Мой предпочтительный метод:

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


0

Самый простой способ - опросить $argvпеременную (что вы, вероятно, все равно будете делать для параметров командной строки). Даже если параметров нет, $argvвозвращается пустой массив.

Если он установлен, то использовался cli. Затем вы можете предположить, что все другие вызовы выполняются через какой-либо веб-сервер или другой.

например:

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

0

Исходя из ответа Silver Moon выше , я использую эту функцию для возврата правильных разрывов строк:

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

}

0

Правильный ответ на этот вопрос зависит от реальных намерений:

  • Является ли SAPI решающим фактором (веб-контекст или нет)?
  • Или информация интерпретируется как «бегущая в tty»?

Если в первом случае даны ответы и комментарии, достаточно найти решение, которое работает.

Если последнее, приведенные здесь рецепты потерпят неудачу, если инструмент запускается как cronjob или background-job от другого демона - в этом случае я предлагаю дополнительно проверить, STDINявляется ли TTY:

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

-1

Как много сложных решений. Как насчет ...

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

-18

Я бы попробовал:

echo exec('whoami');

Обычно веб-серверы запускаются под другим именем пользователя, так что это должно показывать.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.