/--- date: 2017-06-02 19:41:36 +0800 categories: 技术 ---/

PHP脚本编程

环境及配置文件

不论是web server还是shell scripting,PHP通过SAPI层和用户端交互。层级关系如下所示:

PHP SAPI Layout

SAPI有不同的类型,通常提供web服务的被叫做web Server API,而PHP脚本使用的是CLI(Command Line Interface)。CLI模式下,PHP变得很精简,没有提供GET、POST变量,输出也没有MIME头信息, 但是提供了argc、argv变量。

PHP在web Server模式下,fork等进程控制类方法会出现乱用web-server进程资源的问题,因此进程 控制类拓展(pcntl)默认是被禁用的,但是在CLI模式下,因为是一个纯粹的PHP进程,不会出现类似的 问题,所以进程控制类拓展默认是开启、可用的。

在UNIX-like系统上,PHP寻找php.ini配置文件的先后顺序是:首先寻找etc/php-cli.ini,如果 没找到,就寻找etc/php.ini。其中etc对于编译安装php的情况,是自定义安装目录中的etc目录, 否则就是系统的/etc目录。这样做的好处是,web server和CLI 脚本可以使用独立的配置文件。可以 使用php --ini命令来查看加载的配置文件,在php代码中可以使用get_cfg_var("cfg_file_path”) 来查看。(在《PHP 5 power programming》中还指出会在/usr/local/lib目录下面寻找,我并没 有实验出来。我觉得这样处理的好处同firewalld的配置模式 一样)。

实战

PHP没有提供getopt方法用于获取命令行参数,但是PEAR提供了一个叫Console_Getopt的包,可以 用来获取短、长形式的选项。而且已经随着PEAR默认安装在PHP中。使用示例如下:

#!/usr/local/php715/bin/php
<?php
require_once "Console/Getopt.php";

$verbose = 1;
$config_file = 'conf/myrc';
$options = Console_Getopt::getopt($argv, 'hqvc:',array('help', 'quiet', 'verbose','config='));
var_dump($options);
foreach ($options[0] as $opt) {
        var_dump($opt);
        switch ($opt[0]) {
        case 'q': case '--quiet':
                $verbose--;
                break;
        case 'v': case '--verbose':
                $verbose++;
            break;
        case 'h': case '--help':
                usage();
            exit;
        case 'c': case '--config':
            $config_file = $opt[1];
            break;
        }
}

if ($verbose > 1) {
    print "Config file is \"$config_file\".\n";
}

// rest of the script code goes here
function usage() {
    $stderr = fopen("php://stderr", "w");
    $progname = basename($GLOBALS['argv'][0]);
    fwrite($stderr, "Usage: $progname [options]
        Options:
        -q, --quiet                     be less verbose
        -v, --verbose                   be more verbose
        -h, --help                      display help
        -c <file>, --config=<file>      read configuration from <file>\n"
    );
    fclose($stderr);
}

其中getopt函数返回一个数组,第一个元素也是一个数组,格式为array(array(option, value), …) , 如果选项是一个flag标志的,则其value始终为NULL。

在脚本里可以像下面这样使用PEAR提供的错误处理方法,从而免去自己维护的烦恼。

#!/usr/local/php715/bin/php
<?php
require_once "PEAR.php";

$progname = basename($argv[0]);
PEAR::setErrorHandling(PEAR_ERROR_DIE, "$progname: %s\n");

PHP同样有fork系列的进程控制方法,我们可以基于此开发出类似守护进程那样的PHP脚本,下面是一个 deamonizing的示例。

#!/usr/local/php715/bin/php
<?php

$pid = pcntl_fork();
if ($pid) {
    exit(0);
}

// create new session, detach from shell’s process group
posix_setsid();

// XXX if STD{IN,OUT,ERR} constants become available, these have
// to be closed here.

while (true) {
    error_log("heartbeat\n", 3, "/tmp/test.log");
    sleep(10);
}