1. コマンドを使ってスピードアップ

PHPでは、コマンドを実行するための関数(sytem関数やexec関数)が用意されていますが、このような関数を使ってコマンドをうまく利用すると、プログラムの実行スピードを大幅に上げられる場合があります。

forやforeachによる繰り返し処理は、PHPが比較的苦手とする処理(時間がかかる処理)の一つです。
数が少ないうちはいいですが何万という繰り返しが入ってくると、処理はかなり重くなります。

以下は、ログファイルを分析するプログラム例で、10時台のアクセス数を調べています。
一つ目の方は全部PHPで、二つ目の方はコマンドを利用しています。環境はLinux系OSです。

<?php
//全ての処理をPHPで行う

$start_time getTime();

$file file('access_log');
$counter 0;

foreach (
$file as $data) {
  
//10:xx:xxという文字列があるかどうか
  
if (preg_match('/10:[0-5][0-9]:[0-5][0-9]/'$data)) {
    
$counter++;
  }
}

$end_time getTime();

var_dump($counter);
var_dump($end_time $start_time);

//より正確なタイムスタンプを入手するための関数。
function getTime() {
  
$arr explode(' 'microtime());
  return 
$arr[1] + $arr[0];
}
?>

<?php
//コマンドを利用するプログラム

$start_time getTime();

$cmd 'grep 10:[0-5][0-9]:[0-5][0-9] access_log | wc -l';
exec($cmd$status$result);

$end_time getTime();

var_dump($result);
var_dump($end_time $start_time);


function 
getTime() {
  
$arr explode(' 'microtime());
  return 
$arr[1] + $arr[0];
}

?>

今回の例で使用したコマンドですが、

  • grep (パターン) (ファイル名):正規表現で検索し、マッチした行を出力する。
  • wc (ファイル名やデータ)     :単語数を出力する。「-l」オプションをつけた場合は行数を出力する。

というものです。

また、「|」でコマンドをつなぐと、直前のコマンドの出力結果を次のコマンドの引数に渡します。

プログラムの実行結果ですが、ログファイルが1行だと、

  • コマンド不使用:0.00096607208251953秒
  • コマンド使用  :0.023474931716919秒

となり、繰り返し処理が無ければコマンドを使わない方が速くなりますが、

15000行のログファイルだと、

  • コマンド不使用:0.20867109298706秒
  • コマンド使用  :0.10673499107361秒

と、倍近くの差が出ました。

さらに倍の30000行だと

  • コマンド不使用:0.41866302490234秒
  • コマンド使用  :0.17369604110718秒

となり、繰り返し処理が増えるほど差が大きくなることがわかります。

ちなみに、今回ご紹介したコマンドはLinux系OSのもので、通常Windowsでは使えません。

あくまでPHPのプログラムなので、コマンドを使い過ぎるのはどうかと思いますが、UNIXコマンドには強力なものがたくさんあるので、状況に合わせてうまく使っていきましょう。

2. PEAR::SOAPでWebサービス

Webサービスを構築する上で欠かせないものにSOAPと呼ばれるものがあります。
Simple Object Access Protocolの略でXMLをベースにサービス間のメッセージをやりとりするプロトコルです。今回はこのSOAPをPHPで扱うためのライブラリ、PEAR::SOAPを紹介します。

PEAR::SOAPはまだベータ版なので

pear install soap-beta

でインストールすることが出来ます。では、早速使ってみることにしましょう。
PEAR::SOAPの使い方が分かるように簡単なサンプルを用意しました。このサンプルでは文字列を渡すとその文字列の後ろに日付を返すというものをSOAPを使って通信を行います。

SOAPで通信を行うにはSOAPサーバとSOAPクライアントが必要になります。まずSOAPサーバですが以下のようになります。

soap_server.php
<?php
require_once('SOAP/Server.php');

function 
showDatetime($str){
  
$result $str." ".date("Y-m-d");
  return 
$result;
}

$server = new SOAP_Server();
$server->addToMap('showDatetime'array(), array());
$server->service($HTTP_RAW_POST_DATA);
?>

渡された文字列の後ろに今日の日付を付加し返すという処理を行う、showDatetime関数を作成します。次にSOAP_ServerクラスのaddToMapメソッドでこの関数を登録し、serviceメソッドで実行します。

次にSOAPクライアントですが、以下のようになります。

soap_client.php
<?php
require_once('SOAP/Client.php');

$client = new SOAP_Client('http://localhost/soap_server.php');
$params array('input' => "あいうえお");
$options array('timeout' => 20);
$result $client->call('showDatetime'$params$options);
echo 
$result;
?>

SOAP_Clientクラスの引数に通信する先のURLを指定し、callメソッドを使ってSOAPサーバとやりとりを行います。callメソッドの第一引数はSOAPサーバで用意されている関数名、第二引数はその関数に渡すパラメータ、第三引数には通信のためのオプションになります。
この例では文字列「あいうえお」を渡しサーバとの通信でタイムアウトしないようにタイムアウトの秒数を20秒に設定しています。デフォルトは4秒です。

このsoap_client.phpにアクセスすると、localhostにあるsoap_server.phpへ文字列「あいうえお」を渡しサーバで日付を付加して、その結果を表示します。

以上が簡単なSOAPを使った通信になります。これを見ての通りSOAPサーバでどのような関数を用意しているか分からないとなかなか難しいものがあります。そこで登場するのがWSDLと呼ばれるWEBサービスのインターフェースを記述した定義ファイルです。
PEAR::SOAPではこのWSDLの生成も行ってくれます。WSDLも生成するように先ほどのスクリプトを変更すると以下のようになります。

まずはSOAPサーバですが、

soap_server.php
<?php
require_once('SOAP/Server.php');

function 
showDatetime($str){
  
$result $str." ".date("Y-m-d");
  return 
$result;
}

$server = new SOAP_Server();
$server->addToMap('showDatetime'array('inputString' => 'string'),
array(
'outputString' => 'string'));

if (isset(
$_SERVER['REQUEST_METHOD']) &&
$_SERVER['REQUEST_METHOD']=='POST') {
  
$server->service($HTTP_RAW_POST_DATA);
} else {
  require_once 
'SOAP/Disco.php';
  
$disco = new SOAP_DISCO_Server($server,'showDatetime');
  
header("Content-type: text/xml");
  if (isset(
$_SERVER['QUERY_STRING']) &&
strcasecmp($_SERVER['QUERY_STRING'],'wsdl')==0) {
  echo 
$disco->getWSDL();
  } else {
  echo 
$disco->getDISCO();
  }
  exit;
}
?>

addToMapメソッドの第二、第三引数でそれぞれ渡される値の形式、返す値の形式を指定します。あとはPOSTメソッドでアクセスされたときはSOAP通信を実行し、それ以外のメソッドの場合はWSDLファイルのURLまたはWSDLを出力します。

次にクライアントですが、

soap_client.php
<?php
require_once('SOAP/Client.php');

$wsdl = new SOAP_WSDL('http://localhost/soap_server.php?wsdl',
array(
'timeout' => 20));
$proxy $wsdl->getProxy();
echo 
$proxy->showDatetime("あいうえお");
?>

先ほどと違ってSOAP_WSDLクラスでSOAPサーバと通信を行います。getProxyメソッドでプロキシを作成しサーバで用意されている関数名、この場合はshowDatetimeメソッドで値を渡してその結果を表示します。このようにWSDLが用意されている場合、クライアント側での記述はよりわかりやすいものになります。
また、generateProxyCodeメソッドを使うとWSDLファイルを元に生成されたプロキシクラスを確認することが出来ます。

今回は文字列を渡しそれに日付を付加するという単純なWEBサービスを作成しました。複雑なものも単純なものも基本はほぼ同じです。興味のある方はPEAR::SOAPを使って自作のWEBサービスを作ってみてはいかがでしょうか。

バックナンバーについて

TIPS-MLは、毎週金曜日に更新され、新しい記事が掲載されます。