特点
在线手册:中文 英文
PHP手册

PHP 的命令行模式

Table of Contents

从版本 4.3.0 开始,PHP 提供了一种新类型的 CLI SAPI(Server Application Programming Interface,服务端应用编程端口)支持,名为 CLI,意为 Command Line Interface,即命令行接口。顾名思义,该 CLI SAPI 模块主要用作 PHP 的开发外壳应用。CLI SAPI 和其它 CLI 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 参数。

Note:

由于 --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。在 PHP 5 中,CLI 存在于主文件夹中,名为 php.exe,而 CGI 版本名为 php-cgi.exe

从 PHP 5 起,一个名为 php-win.exe 的新文件随包发布。它相当于 CLI 版本,但是 php-win 不输出任何内容,便不提供控制台(不会弹出“DOS 窗口”)。这种方式类似于 php-gtk。需要使用 --enable-cli-win32 选项来配置它。

Note: 如何得知自己使用的是哪个 SAPI?

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

Note:

在 PHP 4.3.2 中加入了 Unix 的 man 页面。可以在命令行中键入 man php 来查看。

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

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

Usage: php [options] [-f] <file> [--] [args...]
       php [options] -r <code> [--] [args...]
       php [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]
       php [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]
       php [options] -- [args...]
       php [options] -a

  -a               Run interactively
  -c <path>|<file> Look for php.ini file in this directory
  -n               No php.ini file will be used
  -d foo[=bar]     Define INI entry foo with value 'bar'
  -e               Generate extended information for debugger/profiler
  -f <file>        Parse <file>.
  -h               This help
  -i               PHP information
  -l               Syntax check only (lint)
  -m               Show compiled in modules
  -r <code>        Run PHP <code> without using script tags <?..?>
  -B <begin_code>  Run PHP <begin_code> before processing input lines
  -R <code>        Run PHP <code> for every input line
  -F <file>        Parse and execute <file> for every input line
  -E <end_code>    Run PHP <end_code> after processing all input lines
  -H               Hide any passed arguments from external tools.
  -s               Display colour syntax highlighted source.
  -v               Version number
  -w               Display source with stripped comments and whitespace.
  -z <file>        Load Zend extension <file>.

  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());'
    

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

    Note:

    请仔细阅读以上范例,在运行代码时没有开始和结束的标记符!加上 -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 代码,然后为该文件设置正确的运行属性(例如:chmod +x test)。该方法可以使得该文件能够像外壳脚本或 PERL 脚本一样被直接执行。

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

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

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

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

PHP 4.3.3 以来有效的长选项:

命令行选项
选项名称 长名称 说明
-a --interactive

交互式运行 PHP。如果编译 PHP 时加入了 Readline 扩展(Windows 下不可用),那将会得到一个很好的外壳,包括一个自动完成的功能(例如可以在键入变量名的时候,按下 TAB 键,PHP 会自动完成该变量名)以及命令历史记录,可以用上下键来访问。历史记录存在 ~/.php_history 文件中。

Note:

通过 auto_prepend_fileauto_append_file 包含的文件在此模式下会被解析,但有些限制,例如函数必须在被调用之前定义。

-c --php-ini

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

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

$ php -c /custom/directory/custom-file.ini my_script.php
如果不指定此选项,PHP 将在默认位置搜索文件。

-n --no-php-ini

完全忽略 php.ini。此参数在 PHP 4.3.0 以后有效。

-d --define

用该参数可以自行设置任何可以在 php.ini 文件中设置的配置选项的值,其语法为:

-d configuration_directive[=value]

例子(因版面原因而折行显示):

# 取值部分被省略,将会把配置选项设为 "1"
$ php -d max_execution_time
        -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(1) "1"

# 取值部分为空白,将会把配置选项设为 ""
php -d max_execution_time=
        -r '$foo = ini_get("max_execution_time"); var_dump($foo);'
string(0) ""

# 配置选项将被设置成为任何 '=' 字符之后的值
$  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 --profile-info

激活扩展信息模式,被用于调试/测试。

-f --file

解析并运行 -f 选项给定的文件名。该参数为可选参数,可以省略,仅指明需要运行的文件名即可。

-h and -? --help and --usage 使用该参数,可以得到完整的命令行参数的列表及这些参数作用的简单描述。
-i --info 该命令行参数会调用 phpinfo() 函数并显示出结果。如果 PHP 没有正常工作,建议执行 php -i 命令来查看在信息表格之前或者对应的地方是否有任何错误信息输出。请注意当使用 CGI 摸索时,输出的内容为 HTML 格式,因此输出的信息篇幅较大。
-l --syntax-check

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

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

Note:

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

-m --modules

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

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

[Zend Modules]

-r --run

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

Note:

使用这种形式的 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 向 » http://bugs.php.net/ 报告。注意,当试图将 shell 变量用到代码中或者使用反斜线时仍然很容易碰到问题。

Note:

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

Note:

此选项只用于非常基本的用途。因此一些配置指令(例如 auto_prepend_fileauto_append_file)在此模式下被忽略。

-B --process-begin

在处理 stdin 之前先执行 PHP 代码。PHP 5 新加。

-R --process-code

对每个输入行都执行 PHP 代码。PHP 5 新加。

此模式下有两个特殊变量:$argn$argi$argn 包含 PHP 当前处理的行内容,而 $argi 则包含该行号。

-F --process-file

对每个输入行都执行 PHP 文件。PHP 5 新加。

-E --process-end

在处理完输入后执行的 PHP 代码。PHP 5 新加。

使用 -B-R-E 选项来计算一个项目总行数的例子。

$ find my_proj | php -B '$l=0;' -R '$l += count(@file($argn));' -E 'echo "Total Lines: $l\n";'
Total Lines: 37328

-s --syntax-highlight and --syntax-highlight

显示有语法高亮色彩的源代码。

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

Note:

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

-v --version

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

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

-w --strip

显示除去了注释和多余空白的源代码。

Note:

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

-z --zend-extension

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

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

Example #1 试图以命令行方式运行的 PHP 脚本(script.php)

#!/usr/bin/php
<?php

if ($argc != || 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 下,可以为此编写一个批处理文件:

Example #2 运行 PHP 命令行脚本的批处理文件(script.bat)

@C:\php\php.exe script.php %1 %2 %3 %

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

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


特点
在线手册:中文 英文
PHP手册
PHP手册 - N: PHP 的命令行模式

用户评论:

me at unreal4u dot com (26-Oct-2011 05:28)

You could use the Linux way of knowing that everything went ok by dying with a numeric code: 0 if everything went ok and practically anything else if something goes terribly wrong. That way;

<?php // hello.php
echo 'hello';
exit(
0);
?>
<?php
// bye.php
echo 'bye';
exit(
1);
?>
<?php
// hello-again.php
echo 'hi world!';
exit(
0);
?>

calling:
php hello.php && php bye.php && php hello-again.php

would only execute the first two scripts, the last one doesn't get executed because an error ocurred in that script.

Greetings.

Kodeart (29-Aug-2011 10:03)

Check directly without calling functions:
<?php
if (PHP_SAPI === 'cli')
{
  
// ...
}
?>

You can define a constant to use it elsewhere
<?php
define
('ISCLI', PHP_SAPI === 'cli');
?>

Anonymous (23-Aug-2011 11:42)

Instead of all these long and .. interesting .. ways to check if you're running from cli, you could always do this:

<?php
function isCli() {
    return
php_sapi_name()==="cli";
}
?>

colet at llsys dot com (29-Jul-2011 03:34)

If checking defined('STDIN') is inconsistent, you could try:
<?php
empty($_SERVER['SHELL']) && die('shells only please');
?>
I haven't tested this on windows.

mniewerth at ultimediaos dot com (25-Jul-2011 03:02)

Regarding the way about the PHP CLI detection, i found out that a test on STDIN will not deliver correct results in every circumstances. That is if we execute the CGI version of PHP on the command line interface, the "defined('STDIN')"-statement will return false. Here is my method to detect the CLI mode.

<?php
class CliCheck
{
   
/**
     * Advanced PHP-CLI mode check.
     *
     * @return boolean    Returns true if PHP is running from the CLI or else false.
     *
     * @access public
     * @static
     */
   
public static function isCli()
    {
       
// If STDIN was not defined and PHP is running as CGI module
        // we can test for the environment variable TERM. This
        // should be a right way how to test the circumstance under
        // what mode PHP is running.
       
if(!defined('STDIN') && self::isCgi()) {
           
// STDIN was not defined, but if the environment variable TERM
            // is set, it is save to say that PHP is running from CLI.
           
if(getenv('TERM')) {
                return
true;
            }
           
// Now return false, because TERM was not set.
           
return false;
        }
        return
defined('STDIN');
    }   

   
/**
     * Simple PHP-CGI mode check.
     *
     * (DSO = Dynamic Shared Object)
     *
     * @link http://httpd.apache.org/docs/current/dso.html DSO
     * @link http://www.php.net/manual/en/function.php-sapi-name.php PHP_SAPI
     *
     * @return boolean    Returns true if PHP is running as CGI module or else false.
     *
     * @access public
     * @static
     */
   
public static function isCgi()
    {
        if (
substr(PHP_SAPI, 0, 3) == 'cgi') {
            return
true;
        } else {
            return
false;
        }
        return
false;
    }
}
print
"PHP is running ".((CliCheck::isCgi()) ? "as CGI modul " : ((!CliCheck::isCli()) ? "as DSO modul " : ""));
print ((
CliCheck::isCli()) ? "from the CLI." : ".");
print
"\n";
?>

volo (19-May-2011 05:35)

Remember that $argv and $argc are not superglobals like $_SERVER. They are not accessible everywhere. If you use them in a function or class, you need to use the typical "global $argv,$argc;" line in order to enable those variables.

Rizwan (20-Apr-2011 12:08)

A simple way to check if script is running from browser or cli.

<?php
if(defined('STDIN') )
  echo(
"Running from CLI");
else
  echo(
"Not Running from CLI");
?>

Anonymous (29-Jun-2010 03:41)

Using CLI (on WIN at least), some INI paths are relative to the current working directory.  For example, if your error_log = "php_errors.log", then php_errors.log will be created (or appended to if already exists) in whatever directory you happen to be in at the moment if you have write access there.  Instead of having random error logs all over the place because of this behavior, you may want to set error_log to a full path, perhaps to the php.exe directory.

ross at golder dot org (23-Dec-2009 06:57)

Note that parsing of the shebang line may not always work as expected...

#!/usr/bin/php -dmemory_limit=512M -dsafe_mode=Off
<?php

print "memory_limit=".ini_get("memory_limit")."\n";
print
"safe_mode=".ini_get("safe_mode")."\n";

?>

gives...

$ ./test.php
PHP:  Invalid configuration directive
memory_limit=512M -dsafe_mode
safe_mode=

notreallyanaddress at somerandomaddr dot com (03-Dec-2009 01:34)

If you want to be interactive with the user and accept user input, all you need to do is read from stdin. 

<?php
echo "Are you sure you want to do this?  Type 'yes' to continue: ";
$handle = fopen ("php://stdin","r");
$line = fgets($handle);
if(
trim($line) != 'yes'){
    echo
"ABORTING!\n";
    exit;
}
echo
"\n";
echo
"Thank you, continuing...\n";
?>

kazink at gmail dot com (02-Dec-2009 11:58)

I had problems running php as CGI in thttpd. I have followed instructions posted by db at digitalmediacreation dot ch, but I was still getting "500 Internal Error" answer from the server. However, I had no problems running php as CLI using a simple wrapper file named index.cgi:

#!/usr/bin/php
<?php
 
require_once 'index.php';
?>

but i needed to pass user data through GET and POST, and this method couldn't handle it. I have spent 2 hours figuring out how to run the CGI mode properly, until I finally gave up, and done it in "manual" way. I have just added some code to the wrapper that reads GET and POST data into the proper variables:

#!/usr/bin/php
<?php

 
//parse the command line into the $_GET variable
 
parse_str($_SERVER['QUERY_STRING'], $_GET);
 
 
//parse the standard input into the $_POST variable
 
if (($_SERVER['REQUEST_METHOD'] === 'POST')
   && (
$_SERVER['CONTENT_LENGTH'] > 0))
  {
   
parse_str(fread(STDIN, $_SERVER['CONTENT_LENGTH']), $_POST);
  }

  require_once
'index.php';
?>

It works well for me. It may be useful if someone else have similar problem.

ca at php dot spamtrak dot org (09-Oct-2009 12:06)

I append this to most of my PHP files, to allow command line unit testing of a class.  It ensures that the unit test is only run if the script is run directly, and won't be triggered by an include from another CLI script.

<?php
if (!empty($argc) && strstr($argv[0], basename(__FILE__))) {
  
$test = new TestClass();
  
$rv = $test->Test();
   die(
"Test returned $rv\n");
}
?>

dj dot rokx at gmail dot com (11-Sep-2009 09:34)

Use PHP as Scripting Language in Windows Vista and 7:

ASSOC .phs=PHPScript
FTPYE PHPScript=[path to]\php.exe -f "%1" -- %*

optional set PATHEXT=.phs;%PATHEXT%

now you can execute any php-script (ext: .phs) from the shell like a .vbs or .cmd.

"c:\testscript.phs arg1 arg2" or with the optional step "c:\testscript arg1 arg2"

i hope this helps somebody.

Wade (07-Sep-2009 06:46)

I've just found that the fact that the CLI does *not* change the current directory will make include() and require() calls with relative paths fail. This is because they are relative to the current directory, not to the current executing file, the documentation notwithstanding. In CGI mode, this is the same because it changes the current directory.

One solution is to call the CGI binary rather than the CLI one. A better solutions is to use dirname(__FILE__) in your path names.

fuzzy76 at fuzzy76 dot net (30-Aug-2009 05:30)

To detect if run from CLI:

if (defined('STDIN'))

or:

if (isset($argc))

patrick at pwfisher dot com (22-Aug-2009 07:59)

This command line option parser supports any combination of three types of options (switches, flags and arguments) and returns a simple array.

[pfisher ~]$ php test.php --foo --bar=baz
  ["foo"]   => true
  ["bar"]   => "baz"

[pfisher ~]$ php test.php -abc
  ["a"]     => true
  ["b"]     => true
  ["c"]     => true

[pfisher ~]$ php test.php arg1 arg2 arg3
  [0]       => "arg1"
  [1]       => "arg2"
  [2]       => "arg3"

<?php
function parseArgs($argv){
   
array_shift($argv);
   
$out = array();
    foreach (
$argv as $arg){
        if (
substr($arg,0,2) == '--'){
           
$eqPos = strpos($arg,'=');
            if (
$eqPos === false){
               
$key = substr($arg,2);
               
$out[$key] = isset($out[$key]) ? $out[$key] : true;
            } else {
               
$key = substr($arg,2,$eqPos-2);
               
$out[$key] = substr($arg,$eqPos+1);
            }
        } else if (
substr($arg,0,1) == '-'){
            if (
substr($arg,2,1) == '='){
               
$key = substr($arg,1,1);
               
$out[$key] = substr($arg,3);
            } else {
               
$chars = str_split(substr($arg,1));
                foreach (
$chars as $char){
                   
$key = $char;
                   
$out[$key] = isset($out[$key]) ? $out[$key] : true;
                }
            }
        } else {
           
$out[] = $arg;
        }
    }
    return
$out;
}
?>

Full version with comments here: http://pwfisher.com/nucleus/index.php?itemid=45

coffear at gmail dot com (07-Jun-2009 01:06)

In the notes it there is an example of running 1 line of PHP using:

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

This might work on a UNIX machine but unfortunately on windows it produces the following error message:

Parse error: parse error in Command line code on line 1

Instead of using ' (single quotes) to encompass the PHP code use " (double quotes) instead. You can safely use ' within the code itself however such as:

php -r "echo 'hello';"

SEWilco (07-May-2009 05:23)

Notice that piping output to some programs will have unexpected behavior:
               php my_script.php | less

The 'less' program usually sets the terminal mode so pressing ENTER is not necessary.  When using php-cli, it is necessary to press ENTER, unless "!stty sane" is able to fix things for you.  The php command is doing something to the terminal mode despite no interactive shell being requested.

Willy T. Koch (24-Feb-2009 10:11)

I'm figuring out how to pipe an email to a php script with postfix. For the email user@example.com:

I created the following line in /etc/aliases:
user:        "|/www/file.php"

file.php is chmod 755

This works fine. But I wanted to test this without having to send an email every time. And this took some searching to figure out, yet it's oh-so simple:

To pipe the file email.txt to the script, write the following in the terminal window:

user@host: php file.php < testepost.txt

I was confused by the | in the aliases file, and didn't get what came after what, etc etc.

Regards,

Willy T. Koch
Norway

patrick smith (11-Nov-2008 03:43)

For command-line option definition and parsing, don't forget about the beauty of getopt().

There's a php-native version (http://php.net/getopt) and a PEAR package -- Console_GetOpt (http://pear.php.net/package/Console_Getopt).

Anonymous (26-Oct-2008 04:52)

Here's  my modification of "thomas dot harding at laposte dot net" script (below) to read arguments from $argv of the form --name=VALUE and -flag.

"Input":
./script.php -a arg1 --opt1 arg2 -bcde --opt2=val2 arg3 arg4 arg5 -fg --opt3

"print_r Output":
Array
(
    [exec] => ./script.php
    [options] => Array
        (
            [0] => opt1
            [1] => Array
                (
                    [0] => opt2
                    [1] => val2
                )
            [2] => opt3
        )
    [flags] => Array
        (
            [0] => a
            [1] => b
            [2] => c
            [3] => d
            [4] => e
            [5] => f
            [6] => g
        )
    [arguments] => Array
        (
            [0] => arg1
            [1] => arg2
            [2] => arg3
            [3] => arg4
            [4] => arg5
        )
)

<?php
function arguments($args ) {
   
$ret = array(
       
'exec'      => '',
       
'options'   => array(),
       
'flags'     => array(),
       
'arguments' => array(),
    );

   
$ret['exec'] = array_shift( $args );

    while ((
$arg = array_shift($args)) != NULL) {
       
// Is it a option? (prefixed with --)
       
if ( substr($arg, 0, 2) === '--' ) {
           
$option = substr($arg, 2);

           
// is it the syntax '--option=argument'?
           
if (strpos($option,'=') !== FALSE)
               
array_push( $ret['options'], explode('=', $option, 2) );
            else
               
array_push( $ret['options'], $option );
           
            continue;
        }

       
// Is it a flag or a serial of flags? (prefixed with -)
       
if ( substr( $arg, 0, 1 ) === '-' ) {
            for (
$i = 1; isset($arg[$i]) ; $i++)
               
$ret['flags'][] = $arg[$i];

            continue;
        }

       
// finally, it is not option, nor flag
       
$ret['arguments'][] = $arg;
        continue;
    }
    return
$ret;
}
//function arguments
?>

tom at thomas dot harding dot net (04-Oct-2008 01:27)

To allow a "zero" option value:

replace:

$ret['options'][$com] = !empty($value) ? $value : true;

by:

$ret['options'][$com] = (strlen($value) > 0 ? $value : true);

In the sample below.

Thanks to Chris Chubb to point me out the problem

thomas dot harding at laposte dot net (14-Jun-2008 11:08)

Parsing command line: optimization is evil!

One thing all contributors on this page forgotten is that you can suround an argv with single or double quotes. So the join coupled together with the