PHPでPath_Classを使ってらくらくディレクトリorファイル操作

先日の記事だけを見ると、Perlを使ったこと無い人からすれば何がどう便利なのか良く分からないと思うのでここらでPath_Classの解説。

まずPath_Classの大きな特徴として、引数で与えたパスが、実際にファイルとして存在してなくても良いというところがあげられるでしょう。

<?php

require_once 'Path/Class.class.php';

// 存在しないパスを与えてもまったく問題なく動く
$dir = cdir('/foo/bar/baz');

// 一個戻ったり
$dir->parent(); # /foo/bar

// 一個進んだり
$dir->subdir('hoge'); # /foo/bar/baz/hoge

// ディレクトリ生成しちゃったり
$dir->mkpath(); # /foo/bar/baz/というディレクトリが作られる。

// ファイルのパスを設定してみたり
$file = $dir->file('aaa.txt'); # /foo/bar/baz/aaa.txt

// 実際に書き込んでみたり
$file->put_contents('hogehoge'); # /foo/bar/baz/aaa.txtにhogehogeと書き込まれる

// 読み込んでみたり
$data = $file->slurp(); # hogehoge

更に「..」を与えてもちゃんと展開するようになっています。

<?php

require_once 'Path/Class.class.php';

// ..も展開してくれる
$dir = cdir('/foo/../bar/baz'); # /bar/baz

PHPにはこのような「..」の展開をやってくれる関数としてrealpathという標準関数が存在していますが、これは実際にそのパスが存在していないと思ったとおりの動作にならないのが難点です。

さてお次は、実際の使用例を上げてみましょう。

日付のディレクトリ名を作り、そこにログを吐くプログラムを考えてみましょう。

まず普通にやってみた場合

<?php

$now = '20100914';
$log_root = '/home/bar/log/';
$log_file = 'log.txt';
$log_data = 'log data';


if ( !file_exists($log_root.DIRECTORY_SEPARATOR.$now) ) {
    // 日付のディレクトリが無ければ作る
    mkdir($log_root.DIRECTORY_SEPARATOR.$now,0777,true);
}

// 書き込み
file_put_contents($log_root.DIRECTORY_SEPARATOR.$now.DIRECTORY_SEPARATOR.$log_file,$log_data);

こんな感じになりますよね。DIRECTORY_SEPARATORが長ったらしくてウザイことこの上ない。

しかも$log_rootは最後に「/」が入ってるのにDIRECTORY_SEPARATORをつけちゃうと「//」になっちゃうので微妙ですよね。かといって毎回最後に「/」が入ってるかどうかをチェックするのもメンドクサイ。

この処理がPath_Classを使うことによってどう変化するか?

<?php
require_once 'Path/Class.class.php';

$now = '20100914';
$log_root = '/home/bar/log/';
$log_file = 'log.txt';
$log_data = 'log data';

$dir = cdir(array($log_root,$now));

if ( !$dir->exists() ) {
    // 日付のディレクトリが無ければ作る
    $dir->mkpath();
}

// 書き込み
$dir->file($log_file)->put_contents($log_data);

はい!どうです?ね?簡単でしょう?

え?行数増えてるだって?おいおい何処を見てるんだよ。行数は確かに増えてるけどタイプ数減ってるだろ!

DIRECTORY_SEPARATORから解放されるだけで物凄くコードが簡素になりましたね。

またディレクトリを再帰的に評価していくのも簡単です。recurseというメソッドに引数としてコールバック用の関数を与えて使います。

<?php

require_once 'Path/Class.class.php';

function foo ($path) {
    print $path->basename()."\n"; // ファイル名のみ表示
}

$dir = cdir('./');
$dir->recurse('foo');

これで「./」以下の全てのファイルを出力できます。

また、あるディレクトリの中身だけが欲しい場合はchildrenメソッドが利用できます。

<?php
require_once 'Path/Class.class.php';

$dir = cdir('./');
foreach($dir->children() as $file) {
    print $file->basename()."\n";
}

recurseと違って、配列の戻り値を返すので、何かとそのまま利用するのに便利です。

あとcdirやcfileという関数が勝手に定義されるが微妙だなという人はPath/Class.class.phpをrequireせずに、Path/Class/Dir.class.php等をrequireすればOKです。

<?php

require_once 'Path/Class/Dir.class.php';

$dir = new Path_Class_Dir('./');

結局のところcdirやcfileという関数はnew Path_Class_Dirやnew Path_Class_Fileの単なるショートカットです。

さてさて、大体主要なメソッドに関してはこれくらいかな。

残りの細かいメソッドに関しては実装見れば大体わかるかなと思います。まぁいずれドキュメントちゃんと書こうとはおもってますがっ・・・!