用 Native PHP 編寫 Command Line (CLI) 程式

Written by Simon Asika on

phpcli.gif

許多 php 套件如 Symfony Console 或 Joomla CLI Application 接已經幫我們封裝好方便的 CLI 介面可以直接使用,這裡來聊聊如果不使用 Framework ,該如何自行操作 PHP IO Stream 來輸入與輸出內容到命令列上面。

安裝 PHP CLI 套件

某些 Linux 的 LAMP 包預設沒有安裝 CLI 模組,可以自行安裝,以 Ubuntu 為例:

apt-get install php5-cli

Windows 可參考: 設定環境變數讓 Windows 命令列可以執行 php 指令

簡單的 PHP 命令列程式

假設我們先寫一個 hello.php 檔案,內容如下:

<?php

echo "Hello World";

接著在命令列執行

$ php hello.php

就會出現

Hello World

非常的簡單。

輸入與輸出資料

PHP 的封裝協定

PHP 提供了一組封裝協定幫助我們存取不同的環境介面,例如 phar:// 可存取 .phar 檔內的資料,file:// 用來存取本地端文件系統,詳情可以參考 PHP 官網介紹

而我們要用的是 php:// 這個協定,它包含了幾個常用的封裝資料:

  1. php://input : 執行時輸入的參數資料,類似 $_REQUEST 的功能。
  2. php://stdout : 執行過程由程式本身輸出到設備上的資料。
  3. php://stdin : 執行過程中要求用戶輸入進來的資料。
  4. php://stderr : 丟出錯誤訊息。

這些封裝資料都是 Resource 的形式,就如同檔案系統一般,我們需要使用 file 函數來存取。

Stdout 標準輸出

假設我們想要輸出資料到命令列上,最簡單的方法當然是直接 echo ,當然充滿 Geek 之驕傲的工程師們可不能這樣做,我們來使用標準輸出印出文字吧:

<?php

$fp = fopen('php://stdout', 'w');

fwrite($fp, "Hello World!!!\n");

fclose($fp);

讓我們執行看看這個檔案,應該會出現:

$ php hello.php
> Hello World!!!
$ 

也就是說,我們只要把 php://stdout 當作檔案操作寫入即可,stdout 是一個 IO Stream ,可以在我們寫入資料時動態反映,因此命令列執行過程會主動輸出寫入的資料到設備上。

Stdin 標準輸入

如果我們想要輸入資料,則必須用 fread 讀取 php://stdin:

<?php

$fh = fopen('php://stdin', 'r');
echo "Please type your name: ";

$str = fread($fh, 1000);  
echo "Your name is: ".$str;  

fclose($fh); 

輸出結果:

$ php hello.php
> Please type your name:

讓我們輸入點字串

$ php hello.php
> Please type your name: Asika
> Your name is: Asika
$ 

當出現: Please type your name: 的當下,程式會暫停執行直到你輸入資料為止。

以下程式碼可以無止盡的要求你輸入資料(笑):

<?php

$fh = fopen('php://stdin', 'r');
echo "Type something: ";

while($str = fread($fh,1000)){
     echo "You type: " . $str;
     echo "Type something: ";
}  

Stderr 標準錯誤

標準錯誤與標準輸出有點像:

<?php

try
{
    throw new RuntimeException('Standard Error');
}
catch(RuntimeException $e)
{
    $fh = fopen('php://stderr', 'w');  

    fwrite($fh, $e->getMessage() . "\n");  

    fclose($fh);
}

直接輸出資料到設備上,不一定要用 Exception ,那只是範例而已。

使用 STD 常數

這三個封裝協定各自有一個代表的常數,分別是: STDIN, STDOUT, STDERR

STDOUT 來說,它代表著指向 php://stdout 的 Resource ,意味著我們連 fopen 都不需要了,直接對它寫入即可,例如原本是這樣:

<?php

$fp = fopen('php://stdout', 'w');

fwrite($fp, "Hello World!!!\n");

fclose($fp);

可以改寫成這樣:

<?php

fwrite(STDOUT, "Hello World!!!\n");

輸入資料更簡單:

echo fgets(STDIN);

非常方便,還不需要 fclose。

接收參數

可接收的參數有兩種,分別是 $argv$argc :

$argv

等同於 $_SERVER['argv'] ,兩者內容一樣,可以取得所有輸入的參數資料,例如我 php 這樣寫:

<?php

print_r($argv);

// OR

print_r($_SERVER['argv']);

接著輸入

$ php hello.php arg subarg -a -b -cd -n name --help --long-param

就會輸出

Array
(
    [0] => hello.php
    [1] => arg
    [2] => subarg
    [3] => -a
    [4] => -b
    [5] => -cd
    [6] => -n
    [7] => name
    [8] => --help
    [9] => --long-param
)

有些 Framework 會額外幫我們把參數處理成 Key Value 的形式,例如下面這樣:

Array
(
    [args] => Array
    (
        [0] => arg
        [1] => subarg
    )
    [params] => Array
    (
        [a] => 1
        [b] => 1
        [c] => 1
        [d] => 1
        [n] => name
        [help] => 1
        [long-param] => 1
    )
)

$argc

$argc 等同 $_SERVEER['argc'] ,只會返回參數數量而已。

其他的一些 php cli 可用參數

大概就先介紹到這裡,應該已經可以寫簡單的應用了,其他還有一些 php 本身好用的參數可用:

  1. -a 直譯模式,可以一行一行輸入程式碼最後再輸出。參考這裡
  2. -v 輸出 php 版本訊息。
  3. -r 直接用一個字串來執行,例如 php -r "echo 'hello';"
  4. -i 輸出 phpinfo ,等同 php -r "phpinfo();"
  5. -h 說明

更多說明請參見 php 官方文件

Control Tools

WS-logo