tohokuaikiのチラシの裏

技術的ネタとか。

PHPでsystem()を走らせたときに、エラーメッセージも取得する方法

システムコマンドを使ったときに、エラーメッセージってどうやって取得するんだろう??
・・・と、PHPのシステムコマンドのマニュアルを見ても出ない。

そういう時は、プロセスをちゃんとみるproc_openを使う。
http://jp.php.net/manual/ja/function.proc-open.php

で作った関数。

<?php
function system_ex($cmd, $stdin = "")
{
    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")
        );

    $process = proc_open($cmd, $descriptorspec, $pipes);
    $result_message = "";
    $error_message = "";
    $return = null;
    if (is_resource($process))
    {
        fputs($pipes[0], $stdin);
        fclose($pipes[0]);
        
        while ($error = fgets($pipes[2])){
            $error_message .= $error;
        }
        while ($result = fgets($pipes[1])){
            $result_message .= $result;
        }
        foreach ($pipes as $k=>$_rs){
            if (is_resource($_rs)){
                fclose($_rs);
            }
        }
        $return = proc_close($process);
    }
    return array(
        'return' => $return,
        'stdout' => $result_message,
        'stderr' => $error_message,
        );
}

コマンド$cmdを走らせて、その結果を

<?php
array(
        'stdout' => $result_message,
        'stderr' => $error_message,
        );

で所得してくれる。エラーメッセージをログに入れておきたい時とか便利。

サンプル

<?php
$cmd = "ls hogehoge"; // こんなファイル・ディレクトリは存在しない
$ret = system_ex($cmd, $input);
var_dump($ret); 
/*
array(2) {
  ["stdout"]=>
  string(0) ""
  ["stderr"]=>
  string(81) "ls: hogehoge: そのようなファイルやディレクトリはありません
"
*/
}

入力をしたい場合

<?php
$cmd = "grep hogehoge ";
$input=<<<EOF
hogehoge
aaahogeaaahoge
bbbhogehogeccc
aaaaaaa
bbbbbbbbbb
EOF;

$ret = system_ex($cmd, $input);
var_dump($ret); 
/*
array(2) {
  ["stdout"]=>
  string(24) "hogehoge
bbbhogehogeccc
"
  ["stderr"]=>
  string(0) ""
}
*/

追記:10年振りに改良

実は、STDOUTが64Kを越えるとデッドロックが掛かるので、修正した。

ポイントは、tmpfile()を使ってファイルポインタを渡したところ。

<?php
function system_ex($cmd, $stdin = "") 
    {

        $descriptorspec = array(
            0 => array("pipe", "r"),
            1 => tmpfile(),
            2 => array("pipe", "w")
            );

        $process = proc_open($cmd, $descriptorspec, $pipes);
        $result_message = "";
        $error_message = "";
        $return = null;
        if (is_resource($process)) 
        {
            fputs($pipes[0], $stdin);
            fclose($pipes[0]);

            while ($error = fgets($pipes[2])){
                $error_message .= $error;
            }
            fseek($descriptorspec[1], 0);
            while ($result = fgets($descriptorspec[1])){
                $result_message .= $result;
            }
            foreach ($pipes as $k=>$_rs){
                if (is_resource($_rs)){
                    fclose($_rs);
                }
            }
            $return = proc_close($process);
        }
        
        if ($return !== 0){
            throw new \Exception("Error in system command.\n => $cmd \n" . $error_message);
        }
        return array(
            'return' => $return,
            'stdout' => $result_message,
            'stderr' => $error_message,
            );
    }