PHP 的命令行模式

来源:常见问题 2014-03-03

从版本 4.3.0 开始,PHP提供了一种新类型的SAPI(Server Application Programming Interface,服务端应用编程端口)支持,名为CLI,意为 Command Line Interface,即命令行接口。顾名思义,该SAPI模块主要用作 PHP 的开发外壳应用。CLI SAPI和其它SAPI模块相比有很多的不同之处,我们将在本章中详细阐述。值得一提的是,CLICGI是不同的 SAPI,尽管它们之间有很多共同的行为。

CLI SAPI最先是随PHP 4.2.0版本发布的,但仍旧只是一个实验性的版本,并需要在运行./configure时加上--enable-cli参数。从PHP 4.3.0版本开始,CLI SAPI成为了正式模块,--enable-cli参数会被默认得设置为 on,您也可以用参数--disable-cli来屏蔽。

从 PHP 4.3.0开始,CLI/CGI 二进制执行文件的文件名、位置和是否存在会根据 PHP 在您系统上的安装而不同。在默认情况下,当运行make时,CGI 和 CLI 都会被编译并且分别放置在您 PHP 源文件目录的sapi/cgi/phpsapi/cli/php下。您可以注意到两个文件都被命名为了php。在make install的过程中会发生什么取决于您的配置行。如果在配置的时候选择了一个 SAPI 模块,如 apxs,或者使用了--disable-cgi参数,则在make install的过程中,CLI 将被拷贝到{PREFIX}/bin/php,除非 CGI 已经被放置在了那个位置。因此,例如,如果在您的配置行中有--with--apxs,则在make install的过程中,CLI 将被拷贝到{PREFIX}/bin/php。如果您希望撤销 CGI 执行文件的安装,请在make install之后运行make install-cli。或者,您也可以在您的配置行中加上--disable-cgi参数。


注: 由于--enable-cli--enable-cgi同时默认有效,因此,您不必再配置行中加上--enable-cli来使得 CLI 在make install过程中被拷贝到{PREFIX}/bin/php

在 PHP 4.2.0 到 PHP 4.2.3 之间的 Windows 发行包中,CLI 的文件名为 php-cli.exe,相同文件夹下的 php.exe 为 CGI。从 PHP 4.3.0 版本开始,Windows 的发行包中 CLI 的执行文件为 php.exe,被放置在一个单独的名为cli的文件夹下,即cli/php.exe


这是哪个 SAPI?: 在命令外壳下,运行php -v便能得知该php是 CGI 还是 CLI。请参考函数 php_sapi_name() 以及常量 PHP_SAPI

注: 在 PHP 4.3.2 中加入了 UNIX 的man页面。可以在你的命令外壳下键入man php来查看。

以下为CLI SAPI和其它SAPI模块相比的显著区别:

  • CGI SAPI不同,其输出没有任何头信息。

    尽管CGI SAPI提供了取消 HTTP 头信息的方法,但在CLI SAPI中并不存在类似的方法以开启 HTTP 头信息的输出。

    CLI 默认以安静模式开始,但为了保证兼容性,-q参数使得您可以使用旧的 CGI 脚本。

    在运行时,不会把工作目录改为脚本的当前目录。(您可以使用-C参数来兼容 CGI 模式)。

    出错时输出纯文本的错误信息(非 HTML 格式)。

  • CLI SAPI强制更改了 php.ini 中的某些设置,因为这些设置在外壳环境下是没有意义的。


    表格 25-1. 被更改的 php.ini 设置选项

    设置选项CLI SAPI默认值备注
    html_errorsFALSE无意义的 HTML 标记符会使得出错信息很凌乱,所以在外壳下阅读报错信息是十分困难的。因此将该选项的默认值改为 FALSE
    implicit_flushTRUE在命令行模式下,所有来自 print()echo() 的输出将被立即写到输出端,而不作任何地缓冲操作。如果您希望延缓或控制标准输出,您仍然可以使用 output buffering 设置项。
    max_execution_time0 (无限值)鉴于在外壳环境下使用PHP的无穷的可能性,最大运行时间被设置为了无限值。为 WEB 开发的应用程序可能只需运行几秒钟时间,而外壳应用程序的运行时间可能会长的多。
    register_argc_argvTRUE

    由于该设置为 TRUE,您将总是可以在CLI SAPI中访问到 argc(传送给应用程序参数的个数)和 argv(包含有实际参数的数组)。

    对于 PHP 4.3.0,在使用CLI SAPI时,PHP变量$argc$argv已被注册并且设定了对应的值。而在这之前的版本,这两个变量在CGI或者模块版本中的建立依赖于将 PHP 的设置选项 register_globals 设为 on。除了版本和register_globals设定以外,您可以随时通过调用 $_SERVER 或者$HTTP_SERVER_VARS来访问它们。例如:$_SERVER['argv']

    注: 这些设置无法在设置文件 php.ini 或任何指定的其它文件中被初始化为其它值。这些默认值被限制在所有其它的设置文件被解析后改变。不过,它们的值可以在程序运行的过程中被改变(尽管对于该运行过程来说,这些设置项是没有意义的)。

  • 为了减轻外壳环境下的工作,我们定义了如下常量:

    表格 25-2. CLI 专用常量

    常量名称描 述
    STDIN一个已打开的指向stdin的流。您可以用如下方法来调用:
    $stdin = fopen('php://stdin', 'r');
    STDOUT一个已打开的指向stdout的流。您可以用如下方式来调用:
    $stdout = fopen('php://stdout', 'w');
    STDERR一个已打开的指向stderr的流。您可以用如下方式来调用:
    $stderr = fopen('php://stderr', 'w');

    有了以上常量,您就无需自己建立指向诸如stderr的流,只需简单的使用这些常量来代替流指向:

    php -r 'fwrite(STDERR, "stderr\n");'

    您无需自己来关闭这些流,PHP 会自动完成这些操作。

  • CLI SAPI会将当前目录改为已运行的脚本所在的目录。

    以下范例显示了本模块与CGI SAPI模块之间的不同:

    <?php
       
    /* Our simple test application named test.php*/
       
    echo getcwd(), "\n";
    ?>

    在使用CGI版本时,其输出为

    $ pwd
    /tmp
    
    $ php-cgi -f another_directory/test.php
    /tmp/another_directory

    我们明显可以看到PHP将当前目录改成了刚刚运行过的脚本所在的目录。

    使用CLI SAPI模式,我们得到:

    $ pwd
    /tmp
    
    $ php -q another_directory/test.php
    /tmp

    这使得我们在利用PHP编写外壳工具时获得了很大的便利。

    注: 您可以在命令行运行时给CGI SAPI加上-C参数,使其支持CLI SAPI的功能。

以下是PHP二进制文件(即 php.exe 程序)提供的命令行模式的选项参数,您随时可以通过 PHP -h 命令来查询这些参数。

Usage: php [options] [-f] <file> [args...]
       php [options] -r <code> [args...]
       php [options] [-- args...]
  -s               Display colour syntax highlighted source.
  -w               Display source with stripped comments and whitespace.
  -f <file>        Parse <file>.
  -v               Version number
  -c <path>|<file> Look for php.ini file in this directory
  -a               Run interactively
  -d foo[=bar]     Define INI entry foo with value 'bar'
  -e               Generate extended information for debugger/profiler
  -z <file>        Load Zend extension <file>.
  -l               Syntax check only (lint)
  -m               Show compiled in modules
  -i               PHP information
  -r <code>        Run PHP <code> without using script tags <?..?>
  -h               This help

  args...          Arguments passed to script. Use -- args when first argument 
                   starts with - or script is read from stdin

CLI SAPI模块有以下三种不同的方法来获取您要运行的PHP代码:

  1. PHP运行指定文件。


    php my_script.php
    
    php -f my_script.php

    以上两种方法(使用或不使用-f参数)都能够运行给定的 my_script.php 文件。您可以选择任何文件来运行,您指定的PHP脚本并非必须要以 .php 为扩展名,它们可以有任意的文件名和扩展名。

  2. 在命令行直接运行PHP代码。


    php -r 'print_r(get_defined_constants());'

    在使用这种方法时,请您注意外壳变量的替代及引号的使用。

    注: 请仔细阅读以上范例,在运行代码时没有开始和结束的标记符!加上-r参数后,这些标记符是不需要的,加上它们会导致语法错误。

  3. 通过标准输入(stdin)提供需要运行的PHP代码。

    以上用法给我们提供了非常强大的功能,使得我们可以如下范例所示,动态地生成PHP代码并通过命令行运行这些代码:

    $ some_application | some_filter | php | sort -u >final_output.txt

以上三种运行代码的方法不能同时使用。

和所有的外壳应用程序一样,PHP的二进制文件(php.exe 文件)及其运行的PHP脚本能够接受一系列的参数。PHP没有限制传送给脚本程序的参数的个数(外壳程序对命令行的字符数有限制,但您通常都不会超过该限制)。传递给您脚本的参数可在全局变量$argv中获取。该数组中下标为零的成员为脚本的名称(当PHP代码来自标准输入获直接用-r参数以命令行方式运行时,该名称为“-”)。另外,全局变量$argc存有$argv数组中成员变量的个数(而非传送给脚本程序的参数的个数)。

只要您传送给您脚本的参数不是以-符号开头,您就无需过多的注意什么。向您的脚本传送以-开头的参数会导致错误,因为PHP会认为应该由它自身来处理这些参数。您可以用参数列表分隔符--来解决这个问题。在PHP解析完参数后,该符号后所有的参数将会被原样传送给您的脚本程序。


# 以下命令将不会运行 PHP 代码,而只显示 PHP 命令行模式的使用说明:
$ php -r 'var_dump($argv);' -h
Usage: php [options] [-f] <file> [args...]
[...]

# 以下命令将会把“-h”参数传送给脚本程序,PHP 不会显示命令行模式的使用说明:
$ php -r 'var_dump($argv);' -- -h
array(2) {
  [0]=>
  string(1) "-"
  [1]=>
  string(2) "-h"
}

除此之外,我们还有另一个方法将PHP用于外壳脚本。您可以在写一个脚本,并在第一行以#!/usr/bin/php开头,在其后加上以PHP开始和结尾标记符包含的正常的PHP代码,然后为该文件设置正确的运行属性。该方法可以使得该文件能够像外壳脚本或 PERL 脚本一样被直接执行。

#!/usr/bin/php
<?php
   var_dump
($argv);
?>

假设改文件名为 test 并被放置在当前目录下,我们可以做如下操作:

$ chmod 755 test
$ ./test -h -- foo
array(4) {
  [0]=>
  string(6) "./test"
  [1]=>
  string(2) "-h"
  [2]=>
  string(2) "--"
  [3]=>
  string(3) "foo"
}

正如您所看到的,在您向该脚本传送以-开头的参数时,脚本仍然能够正常运行。


表格 25-3. 命令行选项

选项名称描 述
-s

显示有语法高亮色彩的源文件。

该参数使用内建机制来解析文件并为其生成一个HTML高亮版本并将结果写到标准输出。请注意该过程所做的只是生成了一个<code> [...] </code>HTML标记符块,并不包含任何的HTML头。

注: 该选项不能和-r参数同时使用。

-w

显示除去了注释和空格的源代码。

注: 该选项不能和-r参数同时使用。

-f

解析并运行给定的文件名。该参数为可选参数且可以不加,仅指明需要运行的文件名即可。

-v

将 PHP、PHP SAPI 及 Zend 的版本信息写入的标准输出。例如:

$ php -v
PHP 4.3.0-dev (cli), Copyright (c) 1997-2002 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2002 Zend Technologies
-c

用该参数,您可以指定一个放置 php.ini 文件的目录,或者直接指定一个自定义的INI文件,其文件名可以不是 php.ini。例如:

$ php -c /custom/directory/ my_script.php

$ php -c /custom/directory/custom-file.ini my_script.php
-a

交互地运行 PHP。

-d

用该参数可以自行设置 php.ini 文件中设置变量的值,其语法为:

-d configuration_directive[=value]

范例:

# Ommiting the value part will set the given configuration directive to "1"
$ php -d max_execution_time 
       -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(1) "1"

# Passing an empty value part will set the configuration directive to ""
php -d max_execution_time= 
       -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(0) ""

# The configuration directive will be set to anything passed after the '=' character
$  php -d max_execution_time=20 
      -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(2) "20"
$  php 
       -d max_execution_time=doesntmakesense 
       -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(15) "doesntmakesense"
-e

为调试器等生成扩展信息。

-z

加载 Zend 扩展库。如果仅给定一个文件名,PHP 将试图从您系统扩展库的默认路径(在 Linux 系统下,该路径通常由 /etc/ld.so.conf 指定)加载该扩展库。如果您用一个绝对路径指定文件名,则系统的扩展库默认路径将不会被使用。如果用相对路径指定的文件名,PHP 则仅试图加载相对于当前目录的扩展库。

-l

该参数提供了对指定PHP代码进行语法检查的方便的方法。如果成功,则向标准输出写入No syntax errors detected in <filename>字符串,并且外壳返回值为0。如果失败,则Errors parsing <filename>以及内部解析器错误信息会一起被写入到标准输出,同时外壳返回值将别设置为255

该参数将无法检查致命错误(如未定义函数),如果您希望检测之名错误,请使用-f参数。

注: 该参数不能和-r一同使用。

-m

使用该参数,PHP 将打印出内置以及已加载的 PHP 及 Zend 模块:

$ php -m
[PHP Modules]
xml
tokenizer
standard
session
posix
pcre
overload
mysql
mbstring
ctype

[Zend Modules]
-i该命令行参数会调用 phpinfo() 函数,并打印出结果。如果PHP没有正常工作,我们建议您执行 php -i 命令来查看在信息表格之前或者对应的地方是否有任何错误信息输出。请注意输出的内容为HTML格式,因此输出的信息篇幅较大。
-r

使用该参数可以在命令行运行PHP代码。您无需加上PHP的起始和结束标识符(<?php?>),否则将会导致语法解析错误。

注: 使用这种形式的PHP时,应个别注意避免和外壳环境进行的命令行参数替换相冲突。

显示语法解析错误的范例

$ php -r "$foo = get_defined_constants();"
Command line code(1) : Parse error - parse error, unexpected '='

这里的问题在于即时使用了双引号",sh/bash 仍然实行了参数替换。由于$foo没有被定义,被替换后它所在的位置变成了空字符,因此在运行时,实际被PHP读取的代码为:

$ php -r " = get_defined_constants();"

正确的方法是使用单引号'。在用单引号引用的字符串中,变量不会被 sh/bash 还原成其原值。

$ php -r '$foo = get_defined_constants(); var_dump($foo);'
array(370) {
  ["E_ERROR"]=>
  int(1)
  ["E_WARNING"]=>
  int(2)
  ["E_PARSE"]=>
  int(4)
  ["E_NOTICE"]=>
  int(8)
  ["E_CORE_ERROR"]=>
  [...]

如果您使用的外壳不是 sh/bash,您可能会碰到其它的问题。请报告您碰到的 bug,或者发邮件到 phpdoc@lists.php.net。

当您试图将外壳的环境变量引入到马或者用反斜线来转义字符时也可能碰到各种各样的问题,请您在使用时注意!

注: -rCLI SAPI 中有效,在 CGI SAPI 中无效。

-h使用该参数,您可以得到完整的命令行参数的列表及这些参数作用的简单描述。

PHP 的命令行模式能使得 PHP 脚本能完全独立于 WEB 服务器单独运行。如果您使用 Unix 系统,您需要在您的 PHP 脚本的最前面加上一行特殊的代码,使得它能够被执行,这样系统就能知道用什么样的程序要运行该脚本。在 Windows 平台下您可以将php.exe.php文件的双击属性相关联,您也可以编写一个批处理文件来用 PHP 执行脚本。为 Unix 系统增加的第一行代码不会影响该脚本在 Windows 下的运行,因此您也可以用该方法编写跨平台的脚本程序。以下是一个简单的PHP 命令行程序的范例。


例子 25-1. 试图以命令行方式运行的 PHP 脚本(script.php)

#!/usr/bin/php
<?php

if ($argc != 2 || in_array($argv[1], array('--help', '-help', '-h', '-?'))) {
?>

This is a command line PHP script with one option.

 Usage:
 <?php echo $argv[0]; ?> <option>

 <option> can be some word you would like
 to print out. With the --help, -help, -h,
 or -? options, you can get this help.

<?php
} else {
   echo
$argv[1];
}
?>

在以上脚本中,我们用第一行特殊的代码来指明该文件应该由 PHP 来执行。我们在这里使用 CLI 的版本,因此不会有 HTTP 头信息输出。在您用 PHP 编写命令行应用程序时,您可以使用两个参数:$argc$argv。前面一个的值是比参数个数大 1 的整数(运行的脚本本身的名称也被当作一个参数)。第二个时包含有参数的数组,其第一个元素为脚本的名称,下标为数字 0($argv[0])。

在以上程序中我们检查了参数的个数是大于 1 个还是小于 1 个。即时参数是--help-help-h-?,我们仍然打印出帮助信息,并同时动态输出脚本的名称。如果还收到了其它参数,我们也把它们显示出来。

如果您希望在 Unix 下运行以上脚本,您需要使得它成为可执行脚本,然后简单的运行script.php echothisscript.php -h。在 Windows 下,您可以为此编写一个批处理文件:


例子 25-2. 运行 PHP 命令行脚本的批处理文件(script.bat)

@c:\php\cli\php.exe script.php %1 %2 %3 %4

假设您将上述程序命名为 script.php,且您的 CLI php.exe 文件放置在 c:\php\cli\php.exe,该批处理文件会帮助您将附加的参数传给脚本程序:script.bat echothisscript.bat -h

请参阅 Readline 扩展模块的有关文档,以获取更多的函数的信息。这些函数可以帮助您完善您的 PHP 命令行应用程序。